rbd: replace O(n) trash list scan with O(1) ID-based trash removal - #6472
Conversation
|
/test ci/centos/mini-e2e-operator/k8s-1.36 |
There was a problem hiding this comment.
Pull request overview
This PR refactors RBD deletion/trash-cleanup paths to avoid O(n) trash-list scans by switching to ID-based trash removal, and propagates parent image IDs to enable faster cleanup of temporary clone images during volume clone workflows.
Changes:
- Add
ParentImageIDtorbdImageand populate it viagetImageInfo()to enable identifying trashed parents by ID. - Make trash removal idempotent on ENOENT and introduce
removeImageFromTrash()as a helper for ID-based trash purge. - Update delete retry/cleanup paths (DeleteVolume/DeleteSnapshot and temp-clone cleanup) to use ID-based trash removal and fix a logging format mismatch.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| internal/rbd/rbd_util.go | Adds ParentImageID, introduces removeImageFromTrash(), updates DeleteTempImage() to remove temp clones from trash by ID, and makes trash removal idempotent. |
| internal/rbd/controllerserver.go | Switches retry/cleanup paths to removeImageFromTrash() and updates a log line to fix formatting. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@Rakshith-R I've been thinking about how to solve this problem lately. This PR is very very nice. |
6c6efd6 to
e0e1fe0
Compare
|
/queue |
|
@Mergifyio rebase |
🛑 The pull request rule doesn't match anymoreDetailsThis action has been cancelled. |
Store the parent image's ID (from GetParent().Image.ImageID) in a new ParentImageID field on rbdImage. This ID is valid even when the parent is in trash, enabling O(1) trash removal without scanning the entire trash list. Reset ParentImageID to empty when GetParent returns ErrNotFound (no parent or parent link severed by flatten). Assisted-by: Claude <noreply@anthropic.com> Signed-off-by: Rakshith R <rar@redhat.com>
Make trashRemoveImage() treat ENOENT as success on both the ceph mgr (AddTrashRemove) and direct (TrashRemove) paths - the image was already purged from trash. The mgr path checks rados.ErrNotFound before isCephMgrSupported to avoid misclassifying ENOENT as a mgr failure. Add removeImageFromTrash() as an O(1) helper that removes a trashed image by its known ID. Assisted-by: Claude <noreply@anthropic.com> Signed-off-by: Rakshith R <rar@redhat.com>
In checkErrAndUndoReserve() and cleanUpImageAndSnapReservation(), use removeImageFromTrash() to remove the trashed image directly by its known ID from the OMAP journal instead of scanning the entire trash list with ensureImageCleanup(). The image ID is always available in OMAP: modern volumes store it at creation time, and legacy volumes get it backfilled by storeImageID() during the first (successful) generateVolumeFromVolumeID() call while the image is still alive. Also fix pre-existing ErrorLog format string mismatch in cleanUpImageAndSnapReservation (2 format verbs but 3 arguments). Assisted-by: Claude <noreply@anthropic.com> Signed-off-by: Rakshith R <rar@redhat.com>
In DeleteTempImage(), use the volume's parent info (ParentImageID) to identify a trashed temp clone by ID instead of scanning the trash list. This works because cleanupRBDImage() calls DeleteTempImage() before rbdVol.Delete(), so the child image still exists and GetParent() data is valid. When parent info is unavailable (temp clone not in trash), treat it as already removed and return success. Remove ensureImageCleanup() entirely - the O(n) GetTrashList scan is no longer needed. All volume/snapshot image IDs are stored in OMAP at creation time and backfilled by storeImageID() on first access, so ImageID is always available for direct O(1) trash removal. Assisted-by: Claude <noreply@anthropic.com> Signed-off-by: Rakshith R <rar@redhat.com>
e0e1fe0 to
0cf959b
Compare
|
/test ci/centos/k8s-e2e-external-storage/1.34 |
|
/test ci/centos/upgrade-tests-cephfs |
|
/test ci/centos/k8s-e2e-external-storage/1.36 |
|
/test ci/centos/mini-e2e-helm/k8s-1.34 |
|
/test ci/centos/k8s-e2e-external-storage/1.35 |
|
/test ci/centos/upgrade-tests-rbd |
|
/test ci/centos/mini-e2e-helm/k8s-1.36 |
|
/test ci/centos/mini-e2e/k8s-1.34 |
|
/test ci/centos/mini-e2e-helm/k8s-1.35 |
|
/test ci/centos/mini-e2e/k8s-1.35 |
|
/test ci/centos/mini-e2e/k8s-1.36 |
| if vol.ParentName != "" { | ||
| depth++ | ||
| } | ||
| vol.RbdImageName = vol.ParentName |
There was a problem hiding this comment.
Now that we have ParentImageID, we need to add an assignment here.
vol.ImageID = vol.ParentImageID
There was a problem hiding this comment.
Now that we have ParentImageID, we need to add an assignment here.
vol.ImageID = vol.ParentImageID
it's not used.
| return nil, err | ||
| } | ||
|
|
||
| image, err := librbd.OpenImage(ri.ioctx, ri.RbdImageName, librbd.NoSnapshot) |
There was a problem hiding this comment.
If the imageID comes from the parentImageID, and the parentImage is in the trash, we should use librbd.OpenImageById(ri.ioctx, ri.ImageID, librbd.NoSnapshot).
There was a problem hiding this comment.
If the imageID comes from the parentImageID, and the parentImage is in the trash, we should use
librbd.OpenImageById(ri.ioctx, ri.ImageID, librbd.NoSnapshot).
There's no use case right now. We can add it when it is required.
There was a problem hiding this comment.
One scenario I'm considering is that when obtaining clone depth, the presence of the parent image in the trash might cause inaccurate clone depth calculations.
There was a problem hiding this comment.
if err != nil {
// if the parent image is moved to trash the name will be present
// in rbd image info but the image will be in trash, in that case
// return the found depth
if errors.Is(err, rbderrors.ErrImageNotFound) {
return depth, nil
}
There's a comment in code regarding this.
Including trashed image in clonedepth calculation will lead to other complexities since trashed image cannot be flattened. Selective flattening logic in cephcsi is already complex. I would prefer it stay as is until there's a issue found relating to the scenario.
|
/retest ci/centos/mini-e2e-helm/k8s-1.36 |
|
Deprecation notice: This pull request comes from a fork and was queued with |
Merge Queue Status
This pull request spent 48 seconds in the queue, including 6 seconds running CI. Required conditions to merge
|
Describe what this PR does
Replace
ensureImageCleanup()- which scans the entire RBD trash listto find an image by name - with direct O(1) removal by image ID using
the new
removeImageFromTrash()helper.The image ID is always available: modern volumes store it in OMAP at
creation time, and legacy volumes get it backfilled by
storeImageID()during the first
generateVolumeFromVolumeID()call.Commit 1 -
rbd: capture parent image ID in getImageInfoAdd
ParentImageIDfield torbdImageand populate it ingetImageInfo()fromGetParent().Image.ImageID. Valid even when theparent is in trash.
Commit 2 -
rbd: make trash remove idempotent and add removeImageFromTrashAdd
removeImageFromTrash()as an O(1) helper that removes a trashedimage by its known ID. Make
trashRemoveImage()treat ENOENT as successon both the ceph mgr and direct paths (image already purged).
Commit 3 -
rbd: avoid trash ls in DeleteVolume/DeleteSnapshot retry pathsIn
checkErrAndUndoReserve()andcleanUpImageAndSnapReservation(),switch from
ensureImageCleanup()toremoveImageFromTrash(). Also fixa pre-existing
ErrorLogformat string mismatch (2 format verbs, 3 args).Commit 4 -
rbd: remove ensureImageCleanup and use ID-based trash removalIn
DeleteTempImage(), use the volume'sParentImageIDto identify atrashed temp clone instead of scanning the trash list. Remove
ensureImageCleanup()entirely - no callers remain.Is there anything that requires special attention
DeleteTempImage()relies on being called beforerbdVol.Delete()sothat
GetParent()data is still valid. This ordering already exists incleanupRBDImage()and is now documented with a comment.DeleteTempImage()(temp clone notin trash), it returns success - same behavior as the old code when the
trash scan found nothing.
The change is backward compatible. No API or OMAP schema changes.
Future concerns
GetTrashListis still used inwaitForCleanTrash()(e2e test helper).That is unrelated to production code paths.
Checklist:
guidelines in the developer
guide.
Request
notes
updated with breaking and/or notable changes for the next major release.
N/A - internal optimization, no user-facing changes.
N/A - no doc changes needed, internal refactor only.
N/A - functions require a live Ceph cluster (librbd calls), not unit-testable.
Existing e2e delete/snapshot tests exercise these code paths.
N/A - existing e2e tests for DeleteVolume, DeleteSnapshot, and volume
cloning already cover the trash cleanup paths exercised by this change.
Show available bot commands
These commands are normally not required, but in case of issues, leave any of
the following bot commands in an otherwise empty comment in this PR:
/retest ci/centos/<job-name>: retest the<job-name>after unrelatedfailure (please report the failure too!)