Repository navigation
fix(storage-cassandra): recover a GSI backfill only in its owner's keyspace - #868
Open
LeeroyHannigan wants to merge 1 commit into
Open
LeeroyHannigan wants to merge 1 commit into
LeeroyHannigan wants to merge 1 commit into
Conversation
…yspace recover_stale_backfills runs once for each account keyspace. Each run read every CREATING index of every account. For an index of another account, it dropped and created the index data table in its own keyspace. It also took a propagation hold there and started a backfill. That backfill failed, because the keyspace has no base table, and the data table stayed. The pass now skips an index whose base table belongs to another account. It skips it before it parses the index definition. poll_gsi_backfill_recovery takes the keyspace to scan as an Option. serve passes None and scans every account keyspace, as before. spawn_gsi_backfill_recovery_for_tests takes an account and scans only that account's keyspace. Before, a worker that one test started could run the backfill of an index that another test created. The other test never fired that worker's token. That made a_backfill_the_recovery_worker_started_ends_before_the_worker_returns fail in the cassandra-storage job. test_ttl_enable_rejects_asynchronous_gsi looked for a refusal wording that #384 replaced. It now checks the table, the index and the delay in the current refusal. The TTL step no longer continues on error. The direct step runs after a failed TTL step, so a run reports both suites.
LeeroyHannigan
requested review from
amrith,
c33howard,
eliseong,
hitesh96db,
jcshepherd,
navareamol,
pdf-amzn and
robinnsc
as code owners
November 5, 2026 18:30
This branch has not been deployed
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What
recover_stale_backfills(crates/storage-cassandra/src/workers.rs) skips aCREATINGindex whose base table belongs to another account. It skips the index before it parses the index definition. Before, each pass over an account keyspace recovered the stale indexes of every account. For another account's index, the pass dropped and created the index data table in its own keyspace and took a propagation hold there. Then it started a backfill. The backfill failed, and the table stayed.poll_gsi_backfill_recoverytakes the keyspace to scan asonly_keyspace: Option<String>.servepassesNoneand scans every account keyspace, as before.spawn_gsi_backfill_recovery_for_teststakes an account and scans only that account's keyspace. The two tests that start a recovery worker pass their own account.recover_stale_gsi_backfills_for_testsruns one pass over one account's keyspace. It returns when the backfills it started end.index::recovery_for_one_account_leaves_the_indexes_of_other_accounts_aloneadds a GSI to one account's table while no worker runs. Then it checks the index after each of these steps:CREATING, with no heartbeat and the same data table.ACTIVE, with its one row.status_index_creationbuilds theUpdateTableinput that addsStatusIndex. It replaces three copies of that input.test_ttl_enable_rejects_asynchronous_gsichecks the table name,asynchronously propagated global secondary indexandindex StatusIndex propagates after 1000 msin the refusal. Before, it looked for a wording that fix(backup): restore sort keys, indexes, and throughput; bound memory; recover abandoned restores #384 replaced..github/workflows/integration-cassandra.yml, the TTL step ofcassandra-storageno longer hascontinue-on-error: true. A failed TTL test now fails the job. The direct step runs after a failed TTL step (if: ${{ !cancelled() }}), so a run reports both suites.Why
cassandra-integrationis not a required check onmain. Before it can be one, its jobs must pass on changes that break nothing.The last 200 runs of the workflow ran from 3 November 13:53 to 5 November 16:43 UTC. In them,
index::a_backfill_the_recovery_worker_started_ends_before_the_worker_returnsfailed 14 of the 175cassandra-storagejobs. Each time it reportedindex rows 2 s after the worker returned: 20000 (ACTIVE). In the same binary,test_a_delete_removes_the_index_row_a_backfill_wrotestarts a second recovery worker, and that worker scanned every account keyspace. Sometimes its pass reached the first test's index before that index had a heartbeat. Then it ran the backfill under its own token, which the first test never fires. The backfill completed and marked the indexACTIVE. #865 has the measurements, and it lists the tables that the scan created in other accounts' keyspaces.The TTL step ignored the failure of a test that fails on every run (#566). It was the only failed TTL test in 130 sampled
cassandra-storagejobs, between 24 October and 5 November.Closes #865
Closes #566
Testing done
Against a cassandra:4.1 node of mine, with the heap sizes of the CI service container:
mainat 38992eb, with 8 account keyspaces and one new GSI, one recovery pass created the index data table in 3 keyspaces. These were the owner's keyspace and 2 others.main, the index wasACTIVE2 s after the probe's own worker returned, in 3 of 3 runs. With this change, it stayedCREATINGin 3 of 3 runs.a worker started for another account recovered the index.the pass over another account's keyspace created index_... there.tokio::spawn, outside theJoinSetand the token. These are the two mutants that d8fc8d7 names. For each, the stop test fails with20000 (ACTIVE).direct_integrationbinary passed 211 of 211, with--test-threads=4.indexmodule passed 19 of 19 in each of 9 runs on one node, with--test-threads=4. The 10th run failed 9 tests with connection timeouts. The node ran out of heap (java.lang.OutOfMemoryError) after about 13 runs without a restart. On a fresh node, the wholedirect_integrationbinary passed 211 of 211, with--test-threads=4.cargo +1.97.0 fmt --all -- --check: clean.cargo +1.97.0 clippy -p extenddb-storage-cassandra --all-targets --features test-hooks -- -D warnings: clean. Pedantic lints flag no line that this change adds.cargo +1.88.0 test --locked -p extenddb-storage-cassandra --features test-hooks --lib --tests --no-run: builds.cargo +1.88.0 test --locked -p extenddb-storage-cassandra --lib: 128 passed.Not in this change:
java.lang.OutOfMemoryError4 times during each of the two whole-binary runs above, and no test failed.Checklist
cargo test --workspace)cargo fmt --check)cargo clippy -- -W clippy::pedantic)Storagetrait, auth model, on-diskformat, or public CLI surface, an RFC has been accepted or is linked
below. Otherwise, an ADR captures the decision (link below).
ADR / RFC: n/a
By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache License 2.0 and I agree to the Developer Certificate of
Origin (DCO). See CONTRIBUTING.md for details.