Skip to content

Add create-only split staging mode - #6714

Draft
Mallets wants to merge 1 commit into
mainfrom
mallets/stage-create-only
Draft

Add create-only split staging mode#6714
Mallets wants to merge 1 commit into
mainfrom
mallets/stage-create-only

Conversation

@Mallets

@Mallets Mallets commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Context

Reconciliation may re-stage a split after a retry or while another reconciliation worker is handling the same split. The existing stage_splits semantics upsert an already-staged row, so a later request can overwrite metadata written by the first worker. In particular, this can replace the row that represents the currently authoritative recovery candidate.

What this changes

Adds a create_only flag to StageSplitsRequest. When enabled, staging inserts only missing split rows and leaves any existing row unchanged. This makes recovery staging idempotent and prevents retries or concurrent reconciliation writers from clobbering an existing split.

The default remains unchanged (create_only: false), preserving the normal upsert behavior for existing callers. The behavior is implemented for both file-backed and PostgreSQL metastores, with coverage for create-only staging.

Testing

  • cargo test -p quickwit-metastore --lib --tests

Expose create-only staging semantics in the metastore API and use them during reconciliation so existing split rows are preserved across retries and concurrent writers.
@Mallets
Mallets marked this pull request as ready for review August 20, 2026 21:24
@Mallets
Mallets requested a review from a team as a code owner August 20, 2026 21:24

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40a4629f2d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

quickwit.common.IndexUid index_uid = 1;
string split_metadata_list_serialized_json = 2;
// Create-only mode: insert missing rows without upserting an existing split row.
bool create_only = 3;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate create-only staging on server support

During a rolling or mixed-version deployment, an older metastore silently ignores unknown protobuf field 3 and executes the existing upsert path, so a new recovery client can overwrite the authoritative split row while receiving a successful response. This semantic mode needs a version-safe RPC or capability gate before callers can rely on it, or an explicitly enforced metastore-first upgrade procedure documented for this protocol change.

AGENTS.md reference: AGENTS.md:L23-L24

Useful? React with 👍 / 👎.

Comment on lines +749 to +751
WHERE splits.split_id = excluded.split_id
AND splits.split_state = 'Staged'
AND NOT $11

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use DO NOTHING for create-only conflicts

When concurrent create-only batches contain the same already-existing split IDs in different orders, this remains an ON CONFLICT DO UPDATE statement, and PostgreSQL locks each conflicting row even though the final predicate is false. The workers can therefore acquire different first locks and deadlock, causing one transaction to abort and consume the metastore client's retry budget even though both requests should be conflict no-ops. Use a create-only query with ON CONFLICT DO NOTHING instead.

Useful? React with 👍 / 👎.

Comment on lines +341 to +342
if self.splits.contains_key(split_metadata.split_id()) {
return Ok(());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report skipped create-only stages as no mutation

When a file-backed create-only request contains only split IDs that already exist, this helper returns the same result as an insertion, and stage_splits consequently returns MutationOccurred::Yes. That makes mutate rewrite the entire index file via put_index for a logical no-op, so repeated recovery retries incur unnecessary storage writes and can fail solely because storage is temporarily unavailable. Return whether an insertion occurred and aggregate that result so an all-skipped batch uses MutationOccurred::No.

Useful? React with 👍 / 👎.

@Mallets
Mallets marked this pull request as draft August 21, 2026 08:42
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