Skip to content

[DPE-9685] Release storage on scale-down#1848

Draft
marceloneppel wants to merge 14 commits into
16/edgefrom
fix/release-storage-on-scale-down
Draft

[DPE-9685] Release storage on scale-down#1848
marceloneppel wants to merge 14 commits into
16/edgefrom
fix/release-storage-on-scale-down

Conversation

@marceloneppel

@marceloneppel marceloneppel commented Jul 10, 2026

Copy link
Copy Markdown
Member

Stacked on #1827 (release storage on teardown); review/merge that first. This branch targets fix/1550-release-storage-on-teardown, so its diff is the commits on top of that base.

Issue

#1827 stops the snap only on full application teardown, so a scaled-down unit (juju remove-unit) still left its storage mounted and Juju could not unmount it (target is busy). This covers the scale-down path for #1550.

Solution

Stop the snap on scale-down too. A stopped unit can no longer be removed from the pysyncobj raft cluster (the leader can no longer resolve its member IP, leaking a ghost member), and a node cannot remove itself from its own endpoint — so the departing unit first drops itself from raft via a surviving peer's endpoint (remote_address=), then stops. When several units are removed at once the first peer tried may itself be departing, so it iterates peers until a reachable node accepts the removal; on a minority scale-down a survivor always remains, so quorum holds. The teardown path is unchanged. Unit and integration tests cover the raft-removal fall-through and the concurrent 2-of-4 removal on the juju40 spread variant.

Checklist

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

The charm implemented no removal hooks, so on unit teardown the
charmed-postgresql snap services kept the Juju storage mounts busy.
Juju's unmount then failed with "target is busy", leaving storage
stuck detaching and blocking machine and model removal (only
destroy-model --force could clear it).

Stop the workload in the storage-detaching hook, which Juju runs
before stop, so the mounts are free by the time Juju unmounts them.
This stops every charmed-postgresql snap service plus the charm's
topology-observer and log-rotation processes, and is idempotent
across the per-storage detaching events.

Fixes #1550.

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

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The storage-detaching regression only manifests on Juju 4.0: 3.6 masks it
with a cleanup_storage shortcut that removes still-Dying storage, and on
4.0 only rootfs (machine-scoped) storage reproduces the stuck unmount, so
running the teardown check on 3.6 proves nothing. Move it out of
test_storage.py into its own spread task pinned to a juju40 variant with
rootfs storage (force-deployed because the charm still declares
assumes: juju < 4).

Also make the list_storage adapter tolerate Juju 4.0's empty list-storage
output, which it prints instead of "{}" for a model with no storage.

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

# Conflicts:
#	src/cluster.py
#	tests/unit/test_cluster.py

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
Stopping the workload in storage-detaching also fired on scale-down
(remove-unit), where the surviving leader still needs the departing
unit's Patroni reachable to remove it from the raft cluster. Stopping it
early broke that reconfiguration, leaving the cluster unable to elect a
primary ("Primary unit not found") and failing the HA/scaling
integration tests.

The storage unmount only actually hangs on full teardown
(remove-application/destroy-model), so guard the handler on
planned_units() == 0. On scale-down it now does nothing, restoring the
pre-fix cluster behaviour, while destroy-model/remove-application still
release the storage.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
Match the one-liner docstring density of the surrounding handlers instead
of an 8-line block (the why lives in the commit that added the guard),
and date the new test file 2026 (it was copied as 2025).

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
@github-actions github-actions Bot added the Libraries: Out of sync The charm libs used are out-of-sync label Jul 10, 2026
stop() leaves the snap services enabled, so a mid-teardown restart of the unit could re-enable them and re-grab the storage mounts before Juju finishes unmounting. stop(disable=True) prevents that.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The task ran only on juju40 (where #1550 reproduces). Add juju36 so the teardown change (stopping/disabling the snap in storage-detaching) is also exercised on 3.6, where the bug is masked by force-removal but a new breakage would still surface.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
On Juju 4.0 storage-detaching runs before the relation-departed and stop hooks and nothing else stops the snap, so a scaled-down unit's storage stayed mounted and Juju could not unmount it. The teardown fix only stops the snap when the whole app is going away, leaving single-unit removals leaked.

Stopping the snap frees the storage, but once stopped the unit can no longer be removed from the pysyncobj raft cluster (the leader can no longer resolve its member IP), leaking a ghost member; a node also cannot remove itself from its own endpoint. So the departing unit drops itself from raft via a surviving peer before stopping. When several units are removed at once the first peer tried may itself be departing, so it iterates peers until a reachable node accepts the removal — on a minority scale-down a survivor always remains and quorum holds.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
Only full-app teardown was exercised for #1550; the scale-down (remove-unit) path — where the departing unit must drop itself from raft via a peer before stopping — had no integration coverage. Add a remove-two-at-once scenario (minority removal, so quorum holds) that asserts the storage is released and the survivors stay active, reusing its deployment for the teardown test to avoid a second cluster.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
destroy_unit returns once the removal is queued; the storage detach/destroy finishes asynchronously on a separate provisioner, so a one-shot check right after wait_for_idle could catch a transient detaching state. Poll until no storage is stuck detaching (the #1550 symptom) — the timeout doubles as the failure signal for a snap-held mount that never unmounts.

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

Removing two units at once on Juju 4.0 can transiently error a departing unit on a canceled hook ('context canceled' as the uniter tears it down) and stall its removal, so the test's wait-for-2-survivors never settled on CI runners (it passed on a bare-metal box, so it is a timing race, not a logic bug). Resolve the departing unit's transient error and keep waiting; a survivor erroring or storage staying stuck still fails the test. The resolve is best-effort and only catches jubilant's CLIError (the unit changed state between the status check and the call).

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
The storage-detaching hook runs before relation-departed/stop on all Juju versions, not just 4.0; what is 4.0-specific is that 3.6 force-removes still-Dying storage (masking the stuck mount) while 4.0 leaves it to unmount. The comment conflated the two.

Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
@marceloneppel marceloneppel force-pushed the fix/release-storage-on-scale-down branch from e490450 to e4f68bc Compare July 15, 2026 13:43
Base automatically changed from fix/1550-release-storage-on-teardown to 16/edge July 16, 2026 12:00
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