Repository navigation
fix(indexer): stop stale deletes removing new resources from search - #122
Merged
Merged
Conversation
Newly created resources could silently go missing from search and stay missing until their next change. A new project was observed being indexed and then deleted about 15ms later by a delete for the same document. The batcher kept upserts and deletes in separate buffers and flushed them as separate, parallel Meilisearch tasks. When an early update queued a delete and a later one queued an upsert for the same document, both were sent and whichever was enqueued last won, often the stale delete. Key changes: - Buffer one pending operation per index and document, so the latest upsert or delete replaces any earlier one by construction. A message is tracked once and acked once, with the batch that carries its operations or the operations that replaced them. - Flush upserts and deletes together as one batch on one flush slot, with the existing message-count and upsert-count triggers. - Chain every batch's enqueue behind the batch taken before it. Meilisearch applies an index's tasks in enqueue order, so an older batch still enqueueing can no longer land after a newer one. Only enqueueing is serialized; waiting on tasks stays concurrent. A batch waits for its turn before taking a flush slot so the in-flight cap cannot deadlock, and a batch abandoned at shutdown hands on its turn and stays unacked. - Batches carrying both operation types report a "mixed" type label on the flush metrics. Ordering holds within one replica only. Events for one resource handled by different replicas, and nack redelivery of an older batch, can still reorder operations.
Every update to any resource fanned a delete out to every index. When an update failed a policy, the indexer queued a delete for that policy's index even if the policy targets a different kind, so each update produced one delete task per index. In production the Project index alone took about 1,550 delete tasks in 90 minutes, almost all deleting nothing. That churn widened the window in which a stale delete could race a newer upsert and remove a newly created resource from search. Key changes: - Queue a delete for a non-matching update or patch only to indexes whose policy targets the object's API group and kind. The version is ignored, since the same object can be written through any served version. - Scope deletes for terminating resources to their kind, read from the response object. - Scope delete-verb events to their API group. Their response is usually a Status and the audit objectRef names only the plural resource, so the kind cannot be determined reliably; every policy in the group still gets the delete.
scotwells
force-pushed
the
fix/indexer-upsert-delete-race
branch
from
October 9, 2026 16:39
71e5bcf to
f82b897
Compare
scotwells
marked this pull request as ready for review
October 9, 2026 17:08
privateip
approved these changes
Oct 9, 2026
kevwilliams
approved these changes
Oct 9, 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 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.
Summary
New projects and other resources could vanish from search right after they were created and stay missing until they changed again. In production, a newly created project was indexed and then deleted about 15ms later by a delete for the same document. This change sends deletes only to the indexes that could hold the resource, and makes sure an older upsert or delete can no longer override a newer one within an indexer replica. That shrinks the race and closes it inside one replica.
Why
Changes
Scope deletes to the resource's type
One latest operation per document
mixedtype label for batches carrying both operation types.Not covered
Test plan
go build ./...,go vet ./...andgo test ./...pass, andgo test ./internal/indexer/... -racepasses three times, including the existing no-split, in-flight bound and ack-progress tests