Skip to content

test(cleanup): run the sweep crash windows on S3 and Azure - #1033

Closed
ragnorc wants to merge 5 commits into
ModernRelay:mainfrom
ragnorc:test/cleanup-sweep-windows-object-stores
Closed

ragnorc wants to merge 5 commits into
ModernRelay:mainfrom
ragnorc:test/cleanup-sweep-windows-object-stores

Conversation

@ragnorc

@ragnorc ragnorc commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #1027: this branch contains its commits, so merge #1027 first. The diff of this PR alone is the last commit.

What

Closes the S3 and Azure part of the Detached-only tables RFC gate "Crash windows inside the sweep, on file, S3 and Azure".

  • The three collector-retry fixtures in crates/omnigraph/tests/failpoints.rs are now helpers over a graph uri. The file tests keep their names (collector_retry_treats_an_index_directory_an_interrupted_pass_removed_as_swept, collector_retry_after_a_pass_stopped_between_manifests_finishes_the_sweep, collector_retry_after_a_pass_stopped_before_the_dead_manifests_removes_them) and call the helpers on a temporary directory.
  • s3_collector_retry_converges_after_every_sweep_window (RustFS failpoints job) and azure_collector_retry_converges_after_every_sweep_window (Azurite job) run all three helpers, each on a fresh graph root. Both jobs now require them by name.
  • The first helper also stops a pass at cleanup.sweep_pre_file_delete, the fourth sweep window, which no failpoint test reached before. The stopped pass deletes nothing, and the next pass plans the same sweep. Its retry now asserts what the other two already asserted: no error on any table and nothing left to remove.
  • Checks that an index directory is present go through StorageAdapter::exists instead of std::fs, so the same assertion works on an object store.
  • helpers::init_and_load_at(uri, schema) is the uri form of init_and_load_with_schema. graph_with_unbuilt_indexes now takes a uri.
  • RFC: the crash-window sentence is removed from the Storage entry of blocked_on. The evidence bullet names the owner for each backend.

Every run checks that the retry reports no error, removes the remaining manifests, and leaves cleanup_plan_missing_paths empty.

Expected green

These tests are coverage. They are not a reproduction of a defect. On S3 and Azure, lance-io 11's ObjectStore::remove_dir_all lists the prefix and batch-deletes what it finds (object_store.rs, the arm for a store with no local directory operations). An absent _indices/<uuid> lists empty, so the retry's delete succeeds without the NotFound arm that #1011 added for the local store. An absent data file or manifest takes delete_object's existing NotFound arm on every backend.

Evidence

Run locally with RUST_MIN_STACK=16777216 on the canonical feature graph (--workspace --exclude omnigraph-gqt --exclude omnigraph-gqt-served --exclude omnigraph-dst --features omnigraph-engine/failpoints,omnigraph-cluster/failpoints --test failpoints):

  • File: the three collector_retry_* tests and the five other collector_* failpoint tests pass. Without a bucket or container, the S3 and Azure cells took their skip.
  • S3: local RustFS 1.0.0-beta.12 (the same digest CI pins), with the env block of the RustFS job. s3_ passes 3 of 3, including s3_collector_retry_converges_after_every_sweep_window, with no skip line. The fixtures wrote 64, 64 and 35 objects under their roots, including 26, 26 and 4 under _indices/.
  • Azure: no Azurite image on this machine, so azure_collector_retry_converges_after_every_sweep_window runs only in the Azurite CI job. Locally it hit the unconfigured skip.
  • cargo clippy on the same graph with -D warnings -W clippy::dbg_macro, cargo fmt --all --check, failpoint_names_guard, check-ci-cells.py (77 names) and check-docs.py all pass.

Reviewer question

With this clause gone, the rest of the Storage entry (the DST oracle and the seven fixtures) appears to be satisfied too, according to the RFC's own Evidence section. I removed only the clause this PR proves and left removing the whole entry to the RFC's owners.

…x directory

The collector deletes a swept version's files before its manifest, so a
pass that stops in between leaves the manifest for the next pass. On a
local store that next pass failed for the table: `delete_paths` called
`remove_dir_all` again on the `_indices/<uuid>` directory the stopped pass
had removed, and Lance's local provider reports an absent directory as
`NotFound`. The table kept the error and deleted nothing on every later
run. Object stores list the prefix empty and succeed, so S3, Azure and
the DST memory store converged already.

An absent index directory is now already deleted, as an absent file
already was. The files-before-manifest order is unchanged, and the
deletion set still holds only paths no retained root marks.

The failpoints test stops a pass at `cleanup.sweep_pre_manifest_delete`
after it removed an index directory only the pruned pin names, then
proves the next pass removes that manifest, reports no error, and leaves
every retained root's paths in place.
Reword the maintenance guide's sentence on an interrupted cleanup so the
deletion order and the next run's outcome read as two steps, and point
the release note at the guide's Cleanup section.
…staging manifests

Cover the directory-touching windows of the collector's sweep on a local
store, where Lance reports removing an absent directory as NotFound:

- a pass stopped between two manifests of the published sweep, with an
  index directory among the files it already deleted;
- a pass stopped after the dead stagings' files and before their
  manifests, through the new seam cleanup.sweep_pre_dead_manifest_delete.

Each retry reports no error, removes the remaining manifests and keeps
every retained root's paths.
…anup-sweep-crash-windows

# Conflicts:
#	crates/omnigraph/tests/failpoints.rs
The three collector-retry fixtures in failpoints.rs become helpers over a
graph uri. The file tests keep their names and call them on a temporary
directory; one RustFS/S3 test and one Azure/Azurite test run all three, each
on a fresh graph root, and both CI jobs require them by name.

The first fixture now also stops a pass at cleanup.sweep_pre_file_delete,
the one sweep window no failpoint test reached: the pass deletes nothing
and the next pass plans the same sweep. Its retry asserts what the other two
already did, no error on any table and nothing left to remove.

Index-directory presence goes through the storage adapter's exists rather
than std::fs, so the same assertion holds on an object store. The RFC's
crash-window gate is satisfied and leaves blocked_on; the evidence bullet
names the owners per backend.
@ragnorc

ragnorc commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1036 (merged as 366be88), which consolidated #1027, #1033 and #1034 on current main and kept their commits. Closing without merging.

@ragnorc ragnorc closed this Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant