volumemgr: retry failed volume deletes instead of leaking them#6176
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #6176 +/- ##
==========================================
+ Coverage 22.62% 23.00% +0.38%
==========================================
Files 506 517 +11
Lines 93198 94953 +1755
==========================================
+ Hits 21082 21841 +759
- Misses 70392 71203 +811
- Partials 1724 1909 +185 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
europaul
left a comment
There was a problem hiding this comment.
mirrors @eriknordmark 's solution for creating the volumes - LGTM
|
Claude finished @rene's task in 3m 19s —— View job Code reviewReviewed the diff against I verified the concurrency-adjacent concern: re-driving while a destroy is still in flight is safe — Two non-blocking observations posted inline:
Neither blocks merge. Nice work — LGTM overall, matching @europaul's approval. |
@europaul , could you please point to which PR are you referring to? |
|
maybeDeleteVolume logged a failed volume delete, set the error, and
unpublished the VolumeStatus anyway ("we have no retrial mechanism for
volume delete now"). Once the status was gone volumemgr had no record of
the volume, so nothing retried and the underlying PVC/Longhorn volume was
leaked forever.
This is how a failed+retried app purge on a kubevirt/longhorn cluster
ends up with several orphaned Longhorn volumes for one app: each purge
bumps the volume generation counter, so GetPVCName() yields a new PVC
name (<VolumeID>-pvc-<gen>) and a new Longhorn volume, while the old
generation's delete was fire-and-forget and dropped on failure. For a
replicated volume the owner node being unreachable at delete time (mid
-purge, or during a node reboot for a cluster update) is enough to
trigger it. The leaked volumes keep replicating and consuming space and
don't show in the Longhorn UI (no PV/PVC/workload binding them).
On delete failure, keep the VolumeStatus published in the Deleting
sub-state with the error and re-drive it from the gc tick via
retryFailedVolumeDelete, mirroring the bounded, gc-driven pattern already
used for transient cluster-volume creates (retryclustervolume.go): a pure
volumeDeleteRetryActionFor decision function, a per-volume retry count in
an in-memory map, and give-up after maxVolumeDeleteRetries so a
permanently-undeletable volume parks terminally rather than resubmitting a
worker job forever. On give-up the volume is left published in the
Deleting sub-state with its error (like the cluster-create give-up) so an
orphaned, undeletable volume stays visible to the operator in status
instead of disappearing; it just stops being re-driven. On successful
delete, or when the volume leaves the candidate set another way (e.g. it
gets re-referenced), the retry count is cleared.
Signed-off-by: Renê de Souza Pinto <rene@renesp.com.br>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
000165c to
1784929
Compare
|
Claude finished @rene's task in 1m 12s —— View job Re-review — both fixes confirmedVerified the two follow-up points from my earlier review are now handled correctly.
A couple of small things I checked while re-reading — both fine, no action needed:
The added unit test pins the boundary ( |
|
Built fault injection + an end-to-end eden test that reproduces the failed-delete scenario this PR fixes, and used them to validate the fix both ways. Tooling (draft PRs):
Results (host EVE-k, current master + this PR):
Negative control — same tooling on master without this PR (branch
One caveat on the give-up test: give-up = |
eriknordmark
left a comment
There was a problem hiding this comment.
I did some fault injection testing to show that EVE master has issues in this space and that they are fixed by this PR.
Description
The core problem is orphaned/leftover Longhorn volumes left behind by a failed "purge" (redeploy/reset) operation.
kubectl get volume, there are three volumes that all share the same distinctive size, the size uniquely allocated to the corrupted VM, indicating these volumes belongs to this VM.The abnormal state:
degraded/unknownrobustness. They were created by the repeated purge attempts.Fix
maybeDeleteVolume logged a failed volume delete, set the error, and unpublished the VolumeStatus anyway ("we have no retrial mechanism for volume delete now"). Once the status was gone volumemgr had no record of the volume, so nothing retried and the underlying PVC/Longhorn volume was leaked forever.
This is how a failed+retried app purge on a kubevirt/longhorn cluster ends up with several orphaned Longhorn volumes for one app: each purge bumps the volume generation counter, so GetPVCName() yields a new PVC name (-pvc-) and a new Longhorn volume, while the old generation's delete was fire-and-forget and dropped on failure. For a replicated volume the owner node being unreachable at delete time (mid -purge, or during a node reboot for a cluster update) is enough to trigger it. The leaked volumes keep replicating and consuming space and don't show in the Longhorn UI (no PV/PVC/workload binding them).
On delete failure, keep the VolumeStatus published in the Deleting sub-state with the error and re-drive it from the gc tick via retryFailedVolumeDelete, mirroring the bounded, gc-driven pattern already used for transient cluster-volume creates (retryclustervolume.go): a pure volumeDeleteRetryActionFor decision function, a per-volume retry count in an in-memory map, and give-up after maxVolumeDeleteRetries so a permanently-undeletable volume parks terminally (unpublished, the pre-existing fallback) rather than resubmitting a worker job forever. On successful delete the retry count is cleared.
How to test and validate this PR
Unit tests were added, the issue is tricky to be reproducible on the field, since purge operation must fail to left an orphan volume on the node.
Changelog notes
Implement deletion of Longhorn orphan volumes created after failed purge operations.
PR Backports
Checklist
check them.