Add optional cursor to DeleteRelationships for resumable batched deletion - #174
Merged
josephschorr merged 1 commit intoSep 10, 2026
Merged
Conversation
…tion Adds optional_cursor to DeleteRelationshipsRequest and after_result_cursor to DeleteRelationshipsResponse, mirroring the cursor pattern already used by ReadRelationships and LookupResources. Together they let a large partial deletion be performed as a series of calls that each resume where the previous one left off, rather than re-examining the relationships already deleted by earlier calls. On datastores whose deletion can be ordered and resumed (e.g. CockroachDB via a primary-key cursor) this avoids the per-batch rescan of prior deletions' tombstones that makes a naive limit loop degrade on large tables. The fields require optional_limit and optional_allow_partial_deletions, and are supported only by datastores that can order and resume deletion; others return an error when a cursor is supplied. The change is additive and backward compatible (buf breaking passes against main). Signed-off-by: Joseph Schorr <josephschorr@users.noreply.github.com>
josephschorr
marked this pull request as ready for review
September 10, 2026 21:15
miparnisari
approved these changes
Sep 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
optional_cursortoDeleteRelationshipsRequestandafter_result_cursortoDeleteRelationshipsResponse, mirroring the cursor pattern already used byReadRelationshipsandLookupResources. Together they let a large partial deletion run as a series of calls that each resume where the previous one left off, instead of re-examining the relationships already deleted by earlier calls.Motivation
Today the partial-deletion loop (
optional_limit+optional_allow_partial_deletions) re-issues the same filter on each call. On CockroachDB that means every batch rescans the MVCC tombstones left by prior batches: the per-batch KV scan cost grows roughly linearly with the number of already-deleted relationships (quadratic total work), which is what makes deleting a large relationship set on a big table degrade over time.A cursor lets each batch resume after the previous one via a primary-key seek, so its scan cost stays flat regardless of accumulated churn. In a controlled tombstone-churn workload on a single-node CockroachDB, the naive loop's per-batch KV scan time climbed ~24x (10ms to 245ms) to delete the same number of rows, while a cursored delete stayed flat (~10ms). The gap widens with scale.
Semantics
optional_limitandoptional_allow_partial_deletions.after_result_cursoris populated only whiledeletion_progressisDELETION_PROGRESS_PARTIAL, and is unset onceDELETION_PROGRESS_COMPLETEis returned.Compatibility
Additive and backward compatible —
buf breakingpasses againstmain.docs/apidocs.swagger.jsonregenerated.Status
Draft: this is the API contract. The SpiceDB server-side implementation (CockroachDB cursored delete) is a follow-up that depends on these fields.