fix: run the empty WAL archive check when an archiver is enabled on an existing cluster by ichibahn · Pull Request #11258 · cloudnative-pg/cloudnative-pg · GitHub
Skip to content

fix: run the empty WAL archive check when an archiver is enabled on an existing cluster - #11258

Open
ichibahn wants to merge 2 commits into
cloudnative-pg:mainfrom
ichibahn:dev/11126
Open

ichibahn wants to merge 2 commits into
cloudnative-pg:mainfrom
ichibahn:dev/11126

Conversation

@ichibahn

Copy link
Copy Markdown

Closes #11126

Problem

A Cluster created without a WAL archiver and given one later never runs the empty-archive check, and starts archiving into a destination that may already hold another cluster's WALs. With the archiver present at bootstrap, the same final spec does run the check — ordering alone decides whether the protection applies.
The check is gated on the .check-empty-wal-archive marker in PGDATA, which reconcileCheckWalArchiveFile removes as soon as ContinuousArchiving turns True. On an archiver-less cluster archive_mode is still on, and wal-archive succeeds as a no-op while reporting that condition with the same ContinuousArchivingSuccess reason a real success uses. The marker is therefore consumed within seconds of bootstrap, and nothing re-creates it when an archiver is enabled later.

Changes

Two commits, 70 lines of non-test code added, 14 removed.

  1. fix(walarchive) — report the archiver-less no-op with a new ContinuousArchivingNotConfigured reason. Status stays True, so kubectl wait --for=condition=ContinuousArchiving, alerting rules and the e2e helpers are unaffected; only reason/message change.

  2. fix(instance) — while the cluster has no potential WAL archiver, keep the marker in place (re-creating it if missing); consume it only on a True condition whose reason is ContinuousArchivingSuccess.
    The second half is load-bearing: re-creating the marker alone is not enough, because the stale True/Success condition left from the archiver-less era deletes the fresh marker on the next reconcile, before the newly enabled archiver runs its first check. I could observe that ordering in the reproduction.

HasPotentialWALArchiver() is deliberately broader than GetEnabledWALArchivePluginName() — any enabled plugin counts, even without isWALArchiver, because archiveWALViaPlugins invokes every enabled plugin declaring the ARCHIVE_WAL capability. A false "has archiver" only costs one extra check; a false "no archiver" could re-arm a check on a working setup.

Relationship to #11216

This builds on #11216 rather than overlapping with it: that PR centralised where the decision is computed (shouldCheckEmptyWalArchive), not what it evaluates, and left the marker's lifecycle untouched. This PR supplies it with a correct marker state. The only overlap with #11216's footprint is a comment update in archiveWALViaPlugins, aligning its description of the marker's lifecycle with the new behaviour.

Verification

kind (1 node, k8s v1.36.1) + MinIO, plugin-barman-cloud v0.13.0 unmodified —the fix is operator-only. Scenario: plugin-less cluster, destination seeded with a foreign object, plugin added later.

before (8046d243d, unpatched, includes #11216) after (this PR)
condition on an archiver-less cluster True(ContinuousArchivingSuccess) True(ContinuousArchivingNotConfigured)
marker lifetime already gone 23s after Ready still there after 5 WAL switches / 2 min
check after enabling the plugin never runs (no trace in the sidecar log) fires 12s after the pod is recreated
WALs into the non-empty destination 2 × 16 MiB, silently none; ContinuousArchiving=False(Failing)
control: plugin present at bootstrap check fires, archiving blocked unchanged
control: empty destination — check runs once, passes, marker consumed, archiving proceeds

The last row is the regression I was most worried about — the marker must still be consumed on a genuine success, or the check would repeat forever. It is.

reconcileCheckWalArchiveFile had no unit coverage; this adds 8 cases, including the stale-condition race and a cluster upgraded from an operator predating the new reason, plus 10 more for HasPotentialWALArchiver and the condition mapping. make lint 0 issues, make test green, make generate manifests produces no diff.

Known limitation

A cluster upgraded from an older operator, where the no-op archiving has not run even once before a plugin is added, still carries a stale True/Success condition and behaves as it does today (check skipped). Not a regression, and it self-heals on the next archiving attempt since the condition is rewritten on every reconcile.

Two design points I'd like a nod on

Raised in the issue; I went with my own judgement so there is something concrete to review, and I'm happy to change either.

  1. The new reason for archiver-less clusters. Status stays True and existing clusters converge on the next reconcile with no migration. If you'd rather keep Success and gate the marker's removal on state persisted elsewhere, I'll rework it.
  2. Removing an archiver re-arms the check for a later re-add. That falls out of the reconciler logic as written; cnpg.io/skipEmptyWalArchiveCheck remains the escape hatch for deliberate destination re-use. If the intended behaviour is the opposite, it is a one-line change.

ichibahn added 2 commits July 28, 2026 14:22
…eason

A cluster with no WAL archiver configured still runs the wal-archive
command, which succeeds as a no-op and reports the ContinuousArchiving
condition with the same ContinuousArchivingSuccess reason used for real
archiving. Downstream consumers cannot tell the two apart, and the
operator wrongly consumes the .check-empty-wal-archive marker file on
the strength of a success that archived nothing (cloudnative-pg#11126).

Compute the distinction in the wal-archive command, from the same cached
cluster the archiver ran with, and report the no-op with the new
ContinuousArchivingNotConfigured reason. The condition status stays True
so that consumers waiting on it behave the same on archiver-less
clusters. Any enabled CNPG-i plugin counts as a potential archiver
(HasPotentialWALArchiver), because the archiving process invokes all
enabled plugins for backward compatibility.

Ref cloudnative-pg#11126

Assisted-by: Claude Fable 5
Signed-off-by: Jihyun Bahn <rring0727@gmail.com>
…n archiver

The .check-empty-wal-archive marker file is created at bootstrap and
removed by reconcileCheckWalArchiveFile as soon as ContinuousArchiving
turns True. On a cluster with no WAL archiver, the no-op archiving path
also produces that condition, so the marker file is consumed within
minutes of bootstrap. Enabling a WAL-archiver plugin later then never
runs the empty-archive check, and the cluster silently archives into a
non-empty destination (cloudnative-pg#11126).

Rework the marker file lifecycle:

- while the cluster has no potential WAL archiver, keep the marker file
  in place and re-create it if missing, so a later-enabled archiver
  still runs the check;
- only consume the marker file on a True condition whose reason is
  ContinuousArchivingSuccess, so the stale no-op condition left over
  from the archiver-less era cannot disarm the check right after an
  archiver is added, before its first archiving attempt.

Removing an archiver now re-arms the check for a later re-add; the
cnpg.io/skipEmptyWalArchiveCheck annotation remains the opt-out for
intentional destination re-use.

Update the marker file description in archiveWALViaPlugins accordingly:
the decision computed there by shouldCheckEmptyWalArchive now sees a
marker file that survives until a configured archiver succeeds.

Add unit tests for reconcileCheckWalArchiveFile, previously uncovered,
including regressions for the stale-condition race and for clusters
upgraded from operators predating the NotConfigured reason.

Fixes cloudnative-pg#11126

Assisted-by: Claude Fable 5
Assisted-by: Claude Opus 5
Signed-off-by: Jihyun Bahn <rring0727@gmail.com>
@ichibahn
ichibahn requested a review from a team July 28, 2026 06:48
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. bug 🐛 Something isn't working release-1.28 release-1.29 release-1.30 labels Jul 28, 2026
@cnpg-bot cnpg-bot added the backport-requested ◀️ This pull request should be backported to all supported releases label Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-requested ◀️ This pull request should be backported to all supported releases bug 🐛 Something isn't working release-1.28 release-1.30 size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Empty WAL archive check is silently skipped when enabling the plugin as WAL archiver on a previously plugin-less cluster

3 participants