Repository navigation
fix(control-plane): do not hold the create-index reply for the plan rebuild - #6857
Open
aayushbaluni wants to merge 2 commits into
Open
aayushbaluni wants to merge 2 commits into
aayushbaluni wants to merge 2 commits into
Conversation
…ebuild `create_index` moved its reply into a task awaiting the next indexing plan rebuild whenever the new index carried a source. That rebuild's completion is signalled by a generation counter, and the counter only advances once every `ApplyIndexingPlan` RPC of that rebuild has finished: `NotifyChangeOnDrop` publishes the generation on Drop, and each apply task holds a clone of it. Apply RPCs to `Ready` indexers carry no deadline, by design -- only Retiring/Decommissioning indexers are time-bound (`indexing_scheduler/mod.rs`, "Ready indexers get no timeout"). So a single Ready indexer that accepted its apply and never answered kept the generation from advancing, and the caller never received a reply for an index that was already committed to the metastore. A retry then failed with `AlreadyExists`, and because that indexer joins every later rebuild, subsequent creates were blocked behind it too. Trigger the rebuild without coupling the reply to it. `rebuild_plan_debounced` already separates the two: it sends `RebuildPlan` through the debouncer and returns a waiter, and that waiter is only a cloned `watch::Receiver`, so dropping it has no effect on the rebuild. This does not change what a caller can observe. `self.model.add_index` runs before the reply either way, so a request issued after the response -- opening shards, for instance -- still sees the index and its sources. The rebuild governs when indexers are told to run pipelines, which was never part of this response. Addresses quickwit-oss#6841
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.
Proposal, not a decision
#6841 offers three directions and the choice is yours. This implements the first one — reply once the metastore write has completed and let the rebuild proceed asynchronously — because I argued for it on the issue and it is the only one that does not touch the apply path.
I am opening it as a concrete thing to react to rather than waiting. Close it freely if you prefer bounding the reply wait, or extending apply deadlines to
Readyindexers, and I will implement whichever you pick instead.What was happening
create_indexmoved its reply into a task awaiting the next plan rebuild whenever the new index carried a source:The rebuild's completion is signalled by a generation counter, and that counter only advances once every
ApplyIndexingPlanRPC of the rebuild has finished —NotifyChangeOnDroppublishes the generation onDrop(indexing_scheduler/change_tracker.rs), and each apply task holds a clone.Apply RPCs to
Readyindexers carry no deadline, deliberately —indexing_scheduler/mod.rsbounds only the draining states and says so: "Ready indexers get no timeout." So one Ready indexer that accepts its apply and never answers keeps the generation from advancing, and the caller never gets a reply for an index already committed to the metastore. A retry returnsAlreadyExists, and since that indexer joins every later rebuild, subsequent creates queue behind it too.The change
Trigger the rebuild without coupling the reply to it.
rebuild_plan_debouncedalready separates those concerns — it sendsRebuildPlanthrough the debouncer and returns a waiter — and the waiter is only a clonedwatch::Receiver, so dropping it cannot affect the rebuild.This does not change what a caller can observe.
self.model.add_indexruns before the reply on both branches already, so a request issued after the response (opening shards, for instance) still sees the index and its sources. The rebuild governs when indexers are told to run pipelines, which was never part of this response.I deliberately left the
Ready-indexer deadline alone. That asymmetry is recorded in a comment, so reversing it is a semantics decision rather than a gap to fill.Verification, and what is missing from it
cargo test -p quickwit-control-plane: 150 passed, 0 failed.cargo clippy -p quickwit-control-plane --all-targetsandcargo +nightly fmt --checkare both clean.Two existing tests already exercise this path with a source —
test_control_plane_handles_rebalance_shards_callbackandtest_control_plane_get_debug_infobothcreate_indexwith aningest_v2source and then immediately open shards. They still pass, which is the evidence that replying earlier does not break a caller that needs the index visible straight away.I could not write a regression test that fails on the old code, and I would rather say so than ship one that looks like it does. I tried two shapes. An indexer backed by an undrained test mailbox does not work: the actor
askcarries a timeout thatUniverse::with_accelerated_timefires immediately, so the apply completes with an error and the generation advances. Replacing it with a directIndexingServiceimpl whoseapply_indexing_planisstd::future::pending().awaitalso still passed on the unfixed code, which I take to mean no apply was dispatched to it at all — with a fresh index and no open shards the plan for that indexer does not change, so the scheduler skips applying and the notifier resolves regardless.Getting a faithful test needs the scheduler to actually dispatch an apply, which needs more setup than I could validate confidently. If you know the shortest way to force that, I will add the test.
Addresses #6841