Skip to content

Dpe 10203 async replication fixes#1837

Draft
marceloneppel wants to merge 5 commits into
16/edgefrom
dpe-10203-async-replication-fixes
Draft

Dpe 10203 async replication fixes#1837
marceloneppel wants to merge 5 commits into
16/edgefrom
dpe-10203-async-replication-fixes

Conversation

@marceloneppel

Copy link
Copy Markdown
Member

Issue

Solution

Checklist

  • I have added or updated any relevant documentation.
  • I have cleaned any remaining cloud resources from my accounts.

After a dead-datacenter failover, re-establishing async replication to a new
cluster failed on create-replication with "committing requested changes
failed" / "secret with label async-replication-secret already exists".

The offer/primary and consumer/standby sides shared one fixed Juju secret
label. A cluster that had been a standby keeps that label reserved as a
consumer alias; Juju does not release it when the dead primary's relation is
torn down. When the cluster is later promoted and owns the shared secret, the
owner-create reuses the same label and deadlocks: the label is simultaneously
unreadable (the aliased remote secret is gone) and uncreatable (the alias still
reserves it).

Own the secret under a distinct label so an owner-create can never collide with
the consumer alias; the consumer keeps reading by secret id, so the handoff is
unchanged and a wedged cluster self-heals without a redeploy.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
…eardown

When a dead datacenter is force-removed, Juju may never deliver the
relation-broken event for the cross-model relation, so the promoted-cluster
-counter set during promotion is left behind. A later, newly-formed async
relation then re-counts that stale value and create-replication wrongly fails
with "There is already a replication set up", blocking recovery.

Reconcile the orphaned counter from the update-status handler, deciding solely
from peer data (no Patroni call) because Patroni is frequently unreachable right
after a dead-DC promotion — exactly when this must still run.

Also harden relation-broken: building the Patroni client can transiently fail
during a force-removal (network-get, goal-state, the REST API), which crashed
the hook and wedged both units in error forever, so update-status never ran and
the cluster never reverted to standalone. Tolerate those failures so the counter
is always cleared.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
After a dead-DC teardown (the primary datacenter force-killed and its
offer cleared with remove-saas --force), the local cross-model
replication relation lingers in a dying state whose databags return
"permission denied" on every relation-get — yet get_relation still
returns it and its active flag can still read True. The charm read that
relation's data unconditionally in _get_primary_cluster (the remote app
databag) and in _relation (used by every write path), so the unhandled
ModelError crashed the charm in __init__ on every hook. All hooks then
failed before update-status could run clear_stale_promotion, so a
promoted primary never recovered and DPE-10203's create-replication
could not proceed.

Guard both reads: _get_primary_cluster skips a peer whose databag raises
ModelError, and _relation probes a cheap own-unit read and treats an
unreadable relation as absent so a promoted primary reconciles as a
standalone cluster. Unit tests cover both guards.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
A force-promote of a standby cluster bumps the promoted-cluster-counter but, while
the dead-DC async relation still lingers, takes the _handle_replication_change path
that never calls promote_standby_cluster() — so the Patroni DCS keeps the stale
standby_cluster from before the promotion. The update_config reconciler only ever
SET standby_cluster (when another cluster is primary) and never CLEARED it (when
this cluster is primary), so the stale entry was never reconciled away and the
promoted cluster stayed a read-only standby leader, blocking dead-DC recovery
(DPE-10203).

Patch standby_cluster to None when this cluster is the primary (no primary
endpoint) so the next update-status converges the DCS after a promote.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The dead-DC teardown scenario (kill primary DC, force-promote standby,
remove-saas --force, re-replicate to a fresh cluster) now recovers end-to-end
with the standby_cluster-clearing fix: each cluster is 2 PG + 1 watcher (the
stereo-mode topology the watcher is built for — 3-member odd raft that keeps
quorum through the DC death), and the promoted cluster converges to a read-write
primary so create-replication to the third cluster succeeds. Re-add the test as
green regression coverage for DPE-10203.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
@marceloneppel marceloneppel force-pushed the dpe-10203-async-replication-fixes branch from ef98cd6 to 93e8dce Compare July 8, 2026 18:55
@github-actions github-actions Bot added the Libraries: Out of sync The charm libs used are out-of-sync label Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Libraries: Out of sync The charm libs used are out-of-sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant