Skip to content

fix(linstor): verify resource deletion completes; warn if stuck in DELETING - #13076

Merged
DaanHoogland merged 2 commits into
apache:mainfrom
jmsperu:feature/linstor-delete-verify
May 21, 2026
Merged

fix(linstor): verify resource deletion completes; warn if stuck in DELETING#13076
DaanHoogland merged 2 commits into
apache:mainfrom
jmsperu:feature/linstor-delete-verify

Conversation

@jmsperu

Copy link
Copy Markdown
Collaborator

Summary

The LINSTOR plugin treats a successful HTTP response from resourceDefinitionDelete as proof the resource is gone and immediately drops the volume from CloudStack's accounting. In practice LINSTOR can return success while the resource lingers in DELETING state — for example when a DRBD peer is unreachable, quorum was lost, or a satellite is down. The plugin has no retry, no verification, and no sweeper. We've found hundreds of stuck DELETING resources accumulated over weeks because nothing surfaces the divergence between the CS view and the LINSTOR view.

What this PR does

Adds two helpers to LinstorUtil:

  • isResourceDefinitionGone(api, rscName) — existence check via resourceDefinitionList
  • waitForResourceDefinitionDeleted(api, rscName, timeoutMillis) — polls every 1s until the resource is gone OR timeout elapses; returns true on confirmed-gone, false on timeout

Calls waitForResourceDefinitionDeleted from both delete paths:

  • LinstorPrimaryDataStoreDriverImpl.deleteResourceDefinition (driver / management-server side)
  • LinstorStorageAdaptor.deRefOrDeleteResource (host / KVM agent side)

Default timeout 30 seconds (DEFAULT_RD_DELETE_VERIFY_TIMEOUT_MILLIS).

Behaviour

  • Resource deletes within 30s (the normal case) — no log change, no behaviour change.
  • Resource still in DELETING after 30s — emits a WARN naming the resource and pointing the operator at linstor resource list. Returns success to the caller (the CS-side accounting has already moved on; throwing here would create a different inconsistency).
  • Controller transiently unreachable during poll — debug-logs each failed poll, keeps trying until deadline.

What this does NOT do (deferred)

  • Tier-2 sweeper that periodically re-attempts delete on long-stuck resources is not in this PR. The Tier-1 surface here is the minimum needed to make the problem visible in operator logs. A sweeper can land separately if maintainers want it.

Test plan

  • CI build + unit tests pass (no public API changes; only behaviour additions in private methods)
  • Manual: delete a volume on a healthy LINSTOR cluster — no new logs, ~30s shorter than wait would otherwise be (immediate return after first poll confirms gone)
  • Manual: delete a volume on a cluster with a downed satellite — observe the new WARN line in the agent log and confirm the resource is still in linstor resource list until manually cleared

…stuck
The LINSTOR plugin treats a successful HTTP response from
resourceDefinitionDelete as proof the resource is gone, then drops
the volume from CloudStack's accounting. In practice LINSTOR can
return success while the resource lingers in DELETING state — for
example when a DRBD peer is unreachable, quorum was lost, or a
satellite is down. The plugin had no retry, no verification, and no
sweeper. Operators have been finding hundreds of stuck DELETING
resources accumulated over weeks because nothing surfaced the
divergence between the CS view and the LINSTOR view.
This change adds two helpers to LinstorUtil:
isResourceDefinitionGone(api, rscName)
- quick existence check via resourceDefinitionList
waitForResourceDefinitionDeleted(api, rscName, timeoutMillis)
- polls every second until the resource is gone OR timeout elapses
- returns true on confirmed-gone, false on timeout
and calls waitForResourceDefinitionDeleted from both delete sites
(driver: LinstorPrimaryDataStoreDriverImpl.deleteResourceDefinition;
adaptor: LinstorStorageAdaptor.deRefOrDeleteResource) with a 30s
default timeout. On timeout the plugin logs a WARN with the resource
name and a hint pointing at `linstor resource list`. We deliberately
do NOT throw on timeout: the CS-side accounting has already moved
on, and throwing would create a different inconsistency.
This is the minimal Tier-1 fix that surfaces the problem in the
operator's view. A follow-up could add a periodic sweeper that
attempts force-delete on long-stuck DELETING resources.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds post-delete verification for LINSTOR resource definitions to surface cases where LINSTOR returns success but the resource definition remains stuck in DELETING, improving operational visibility while keeping CloudStack-side deletion semantics unchanged.

Changes:

  • Add isResourceDefinitionGone and waitForResourceDefinitionDeleted helpers (with a 30s default timeout) to poll for confirmed deletion.
  • Invoke the delete-verification helper from both management-server and KVM agent delete paths.
  • Emit WARN logs when the resource definition still exists after the verification timeout.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

FileDescription
plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.javaAdds delete-verification helpers and a default timeout constant.
plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.javaVerifies RD deletion after successful API delete; warns if still present.
plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.javaVerifies RD deletion after successful API delete in agent path; warns if still present.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +432 to +452
public static boolean waitForResourceDefinitionDeleted(DevelopersApi api, String rscName, long timeoutMillis) {
final long deadline = System.currentTimeMillis() + timeoutMillis;
while (true) {
try {
if (isResourceDefinitionGone(api, rscName)) {
return true;
}
} catch (ApiException e) {
LOGGER.debug("LINSTOR delete-verify poll failed for {}: {}", rscName, e.getMessage());
// Keep polling — controller may be transiently unavailable.
}
if (System.currentTimeMillis() >= deadline) {
return false;
}
try {
Thread.sleep(1_000L);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return false;
}
}

CopilotAIApr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New delete-verification behavior in waitForResourceDefinitionDeleted / isResourceDefinitionGone isn’t covered by the existing LinstorUtilTest suite. Please add unit tests for (a) confirmed-gone case, and (b) timeout/exception paths (ideally without introducing real sleeps) so regressions in the polling logic are caught in CI.

Copilot uses AI. Check for mistakes.
@codecov

codecovBot commented Apr 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 31 lines in your changes missing coverage. Please review.
✅ Project coverage is 18.08%. Comparing base (6f4445c) to head (b7be64f).
⚠️ Report is 28 commits behind head on main.

Files with missing linesPatch %Lines
...cloudstack/storage/datastore/util/LinstorUtil.java0.00%23 Missing ⚠️
.../hypervisor/kvm/storage/LinstorStorageAdaptor.java0.00%4 Missing ⚠️
...tore/driver/LinstorPrimaryDataStoreDriverImpl.java0.00%4 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #13076 +/- ##
============================================
+ Coverage 18.02% 18.08% +0.05% - Complexity 16621 16719 +98 
============================================
Files 6029 6037 +8 Lines 542184 542611 +427 Branches 66451 66434 -17 ============================================
+ Hits 97740 98120 +380 - Misses 433428 433476 +48 + Partials 11016 11015 -1 
FlagCoverage Δ
uitests3.51% <ø> (-0.01%)⬇️
unittests19.24% <0.00%> (+0.06%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@rp-rp- left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you do the one suggested change, I can ran it against Linstor tests

resourceDefinitionList(null, ...) fetched every RD on the cluster and
then streamed for a match — repeated once per second from
waitForResourceDefinitionDeleted this scales with cluster size. Pass
the name as the rscDfns filter so the controller returns an empty
list when the RD is gone.
Also clarify the timeout-constant Javadoc per copilot/abh1sar review
(constant is used from management-server paths too, not just agents).
Addresses review comments from @rp- and Copilot on apache#13076.
@sureshanaparti

Copy link
Copy Markdown
Contributor

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@sureshanaparti a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17858

@DaanHoogland

Copy link
Copy Markdown
Contributor

@rp- , will you run tests for this one?

@rp-

rp- commented May 20, 2026

Copy link
Copy Markdown
Contributor

@rp- , will you run tests for this one?

All fine here, LGTM

@DaanHoogland

Copy link
Copy Markdown
Contributor

@rp- , will you run tests for this one?

All fine here, LGTM

so your “request changes” status is not longer valid?

rp-
rp- approved these changes May 21, 2026
@rp-

rp- commented May 21, 2026

Copy link
Copy Markdown
Contributor

@rp- , will you run tests for this one?

All fine here, LGTM

so your “request changes” status is not longer valid?

sorry, did miss the status

@DaanHoogland
DaanHoogland merged commit 850b443 into apache:mainMay 21, 2026
27 of 28 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@jmsperu@sureshanaparti@blueorangutan@DaanHoogland@rp-@winterhazel