Skip to content

The archives a data plane takes reach the control plane - #216

Merged
NathaelB merged 1 commit into
mainfrom
feat/archives-reach-the-control-plane
Sep 13, 2026
Merged

NathaelB merged 1 commit into
mainfrom
feat/archives-reach-the-control-plane

Conversation

@NathaelB

Copy link
Copy Markdown
Owner

Part of #177. The other half of V4 (#182): the receiving end has existed since #192 and nothing ever called it.

Why nothing was being reported

Two links were missing, and the first is the reason the second had nothing to carry.

Scheduled archives existed nowhere at this layer. A ScheduledBackup creates CloudNativePG Backup objects directly. The Aether kind was only ever created for a one-off archive by reconcile_backup -- and nothing created those either -- so every archive that actually happened was invisible to the platform that scheduled it, manifest included.

Nothing said anything upward. No backup handler in genesis, nothing in herald, and POST /dataplanes/{}/deployments/{}/archive with no caller.

Adoption

A third controller watches CloudNativePG Backup objects. One taken on a cluster this platform runs gets an IdentityInstanceBackup to be seen through, and the controller that was already there follows it from that point -- status, manifest and all.

Only for our clusters: a store shared with somebody else's CloudNativePG is a supported situation, and adopting their archives would report backups of databases Aether does not run. The test for that is a_cluster_somebody_else_runs_is_left_alone.

Adoption is recorded on the status, and it means follow this, do not create it. Applying our own Backup over one a ScheduledBackup made would force the ownership across, and deleting the schedule would then stop cascading to the archives it took.

Reporting

Herald is the only component holding credentials for the control plane, so it is the one that reports. It lists the Aether kind -- the only place that says which deployment an archive belonged to -- on its own slow tick (5 minutes by default; an archive is an hourly event at best).

Everything the cluster still knows about is sent on every pass, and nothing is remembered between them. The control plane records an archive once per object key, so repeating is free, and the state this would otherwise keep is state a restart loses. For backups that means an archive that exists and that nobody ever hears about.

One archive nobody could report does not stop the others. A single unreachable deployment would otherwise hide every other backup this data plane took, and that is the rule ArchiveReporter exists to hold -- its own type rather than a sixth method on a service that already carries five collaborators.

Two things the status could not say

The control plane refuses a partial report rather than filling it in, so both had to become real:

  • The Postgres major. Read off the cluster, not the archive: CloudNativePG puts it on neither the Backup nor anywhere else once the cluster is gone. Taken from the number newer versions report, and from the image tag when they do not (ghcr.io/cloudnative-pg/postgresql:17.2-standard-trixie is 17, and a registry carrying a port does not read as a version). An archive that cannot name its engine is one nobody can safely restore, and a physical archive restored onto the wrong major does not fail politely.
  • The manifest key. Recorded rather than derived by whoever needs it. It is what the archive is recorded under, and what makes a redelivered report record it once instead of twice; deriving it in two places is how the two come to disagree.

An attempt that produced nothing is reported too, through the same endpoint. A backup that failed silently cannot be told from one that never ran.

Verified

cargo test --workspace 1084 passed, clippy --all-targets clean, fmt --check clean, chart renders with herald's new read-only rule on identityinstancebackups.

The guard is falsified: defaulting size_bytes instead of requiring it fails an_archive_still_being_written_is_not_reported and nothing else.

Not exercised against a live cluster yet. The next branch puts the list on screen, which is also what makes that easy to check.

Next

`POST /dataplanes/{}/deployments/{}/archive` has existed since the
control plane learned to record an archive, and nothing ever called it.
Two links were missing, and the first one is the reason the second had
nothing to carry.

**Scheduled archives existed nowhere at this layer.** A `ScheduledBackup`
creates CloudNativePG `Backup` objects directly. The Aether kind was only
ever created for a one-off archive -- and nothing created those either --
so every archive that actually happened was invisible to the platform
that scheduled it, manifest included. A third controller now adopts them:
a CloudNativePG `Backup` on a cluster this platform runs gets an
`IdentityInstanceBackup` to be seen through, and the controller that was
already there follows it from that point.

Adoption is recorded on the status, and it means "follow this, do not
create it". Applying our own `Backup` over one a `ScheduledBackup` made
would force the ownership across, and deleting the schedule would then
stop cascading to the archives it took.

**Nothing said it upward.** Herald is the only component holding
credentials for the control plane, so it is the one that reports. It
lists the Aether kind -- the only place that says which deployment an
archive belonged to -- on its own slow tick, and sends everything the
cluster still knows about on every pass. Nothing is remembered between
passes: the control plane records an archive once per object key, so
repeating is free, and the state this would otherwise keep is state a
restart loses. For backups that means an archive that exists and that
nobody ever hears about.

Two things the status could not say before, and a report is refused
without either. The Postgres major is read off the cluster, from the
number newer CloudNativePG reports and from the image tag when it does
not -- an archive that cannot name its engine is one nobody can safely
restore. And the manifest key is recorded rather than derived by whoever
needs it: it is what the archive is recorded under, and deriving it twice
is how the two come to disagree.

An attempt that produced nothing is reported too. A backup that failed
silently cannot be told from one that never ran.

`ArchiveReporter` is its own type rather than a sixth method on a service
that already carries five collaborators. One archive nobody could report
does not stop the others, which is the rule it exists to hold.
@NathaelB NathaelB self-assigned this Sep 13, 2026
@NathaelB
NathaelB merged commit ed4c67c into main Sep 13, 2026
5 checks passed
@NathaelB
NathaelB deleted the feat/archives-reach-the-control-plane branch September 13, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant