Skip to content

docs(qwp): document when a NACK replay can duplicate rows - #122

Open
jerrinot wants to merge 2 commits into
mainfrom
jh_nack_doc_partial_batch_commit
Open

jerrinot wants to merge 2 commits into
mainfrom
jh_nack_doc_partial_batch_commit

Conversation

@jerrinot

@jerrinot jerrinot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor
  • The NACK policy doc no longer claims a NACKed frame is never partly stored.
  • It says when a replay stores rows twice, how many at most, and that DEDUP absorbs them.

Tandem: questdb/questdb#8195.

The server commits each table on its own, so a table whose commit fails after others committed leaves their rows from the replayed frames already stored. Since questdb/questdb#8195 one commit spans the plain frames of a whole receive pass, which makes this up to one pass (http.recv.buffer.size, 2 MiB by default); a FLAG_DEFER_COMMIT group can re-send the whole group. The invariant now states this in place of "NACK ⇒ batch atomically not applied".

The replay invariant in design/qwp-nack-policy-v2.md said a NACK means
the server applied nothing of the rejected batch. The server's commit
batch now spans every table of a receive pass and commits the tables one
at a time (questdb#8195). It rejects the whole batch for every refusal
it can check before committing: a read-only node, an ACL denial, or a
dropped, renamed or suspended table. A storage fault on one table's
commit, or a DROP, RENAME or SUSPEND WAL landing while the batch
commits, can still fail a table after others committed. Their rows
from frames at or after the NACKed frame are then applied, and the
client's RETRIABLE replay sends them again.

The invariant now states what a NACK names, when the replay duplicates
nothing, and the exception with its bound: one server receive pass
(http.recv.buffer.size, 2 MiB by default). DEDUP tables absorb the
duplicates. The client's policy does not change: it replays rather
than risk loss.
The invariant packed the ack rule, the pre-commit refusals and the
partial-commit exception into one long sentence chain, and it no longer
matched the server: questdb#8195 now checks a table's refusals as the
table joins the commit batch, and a DROP, RENAME or SUSPEND WAL can fail
a table at any time while it waits for its commit, not only mid-commit.

The bullet now states the ack rule, what a RETRIABLE replay normally
re-sends, when it re-sends rows the server already stored, and what
bounds them: one server receive pass for plain frames, or the whole
group for a FLAG_DEFER_COMMIT group, which the old text left out.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation QWP tandem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant