Repository navigation
[fix](cloud) Fence MetaService before giving up the TSO of a commit that failed with KV_TXN_COMMIT_ERR - #69925
morningman wants to merge 1 commit into
Conversation
…hat failed with KV_TXN_COMMIT_ERR ### What problem does this PR solve? Issue Number: None Related PR: apache#67820, apache#69831 Problem Summary: In cloud mode the Master FE registers the commit TSO of each transaction it sends to MetaService and keeps a committed TSO watermark below the oldest registered one. A bounded `@incr` read admits a window that ends at or below the watermark without waiting, on the guarantee that no commit with a TSO at or below it can still land. apache#67820 keeps that guarantee by giving up a TSO only once the outcome of its commit is settled: a success, an already visible or an already aborted answer finishes it; a failed RPC fences MetaService at the TSO first (`fenceAndAbandonCommitTso`), as commit_txn reads the fence in the FDB transaction it commits, so the commit has landed before the fence or is rejected by it; any other answer gives the TSO up at once, as a commit that failed for good. `KV_TXN_COMMIT_ERR` is not always such an answer. MetaService gives each FDB transaction a timeout (`fdb_txn_timeout_ms`, 10 seconds by default) and answers `KV_TXN_COMMIT_ERR` when the commit of the transaction timed out (FDB errors 1004 and 1031) or failed with an error it does not classify. A timeout cancels the FDB transaction, and a commit cancelled after it was sent may have happened or may still happen. FE gave such a TSO up without a fence, so the watermark could pass a commit that lands later, and a bounded `@incr` read admitted at that watermark would miss its rows. `KV_TXN_COMMIT_ERR` now takes the path of a failed RPC: MetaService is fenced at the TSO before it is given up. A fence that fails deactivates the TSO service, as on a failed RPC, so no read is admitted on the watermark until a calibration fences MetaService above every TSO given out before. The commit fails as before, with the fence error when the fence fails; the answer of MetaService is logged first then. | Answer of MetaService to commit_txn | Before | After | | --- | --- | --- | | `KV_TXN_COMMIT_ERR` | TSO given up | MetaService fenced at the TSO, then the TSO given up | | Failed RPC | MetaService fenced at the TSO, then the TSO given up | Unchanged | | `KV_TXN_MAYBE_COMMITTED` and any other error | TSO given up | Unchanged | | `OK`, `TXN_ALREADY_VISIBLE`, `TXN_ALREADY_ABORTED` | Transaction finished | Unchanged | ### Release note Fix a bounded incremental read in cloud mode that could miss a transaction whose commit in MetaService timed out. ### Check List (For Author) - Test: Unit Test - `CloudCommittedTsoTest#testKvCommitErrorIsFencedButDefiniteFailureIsReleased`: `KV_TXN_COMMIT_ERR` fences MetaService before the TSO is given up and still fails the commit; a fence that fails fails the commit with the fence error and gives nothing up; `KV_TXN_TOO_OLD` still gives the TSO up without a fence. It fails without the fix. The other three tests of the class pass. - Behavior changed: Yes. A commit that MetaService answers with `KV_TXN_COMMIT_ERR` costs one more MetaService RPC, the fence, and the commits in flight at or below the fenced TSO are rejected once and retried with a new TSO, as after a failed RPC. If the fence fails, the TSO service deactivates until it is calibrated again. - Does this need documentation: No Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
run buildall |
|
/review |
There was a problem hiding this comment.
The change fixes the data-correctness hazard for genuinely ambiguous MetaService commit outcomes: CloudGlobalTransactionMgr now fences MetaService before releasing the commit TSO, and fence failures propagate through the existing FE cleanup path. The new unit test covers the uncertain response, fence failure, and definite KV_TXN_TOO_OLD release.
One P2 availability issue remains inline. MetaService also collapses exhausted KV_TXN_STORE_COMMIT_RETRYABLE responses originating from TXN_RETRYABLE_NOT_COMMITTED into KV_TXN_COMMIT_ERR. The FE branch therefore fences a known-not-committed attempt, unnecessarily invalidating lower in-flight TSOs and risking retry fan-out or TSO-service deactivation when the extra fence RPC fails.
Review checkpoints: the cloud transaction lifecycle and TSO tracker ordering were traced through commitTxn, MetaService retry/status normalization, advanceTsoFence, and TSOService; fencing precedes exact tracker removal and failure deactivates the service safely. No lock-order, callback, persistence, FE-BE protocol, or non-cloud parallel-path regression was found in this two-file patch. Error propagation is checked at the fence boundary. The added test setup and status-code normalization were independently rechecked. No user-provided focus points or existing inline blocking findings were present. Review was static only; no builds or tests were executed.
Review converged after 2 round(s).
| } else if (code == MetaServiceCode.OK || code == MetaServiceCode.TXN_ALREADY_VISIBLE | ||
| || code == MetaServiceCode.TXN_ALREADY_ABORTED) { | ||
| Env.getCurrentEnv().getTSOService().markTxnFinished(commitTxnRequest.getDbId(), transactionId); | ||
| } else if (code == MetaServiceCode.KV_TXN_COMMIT_ERR) { |
There was a problem hiding this comment.
[P2] Avoid fencing known-not-committed retry exhaustion
MetaService::call_impl also rewrites an exhausted KV_TXN_STORE_COMMIT_RETRYABLE—which cast_as<COMMIT> produces for TxnErrorCode::TXN_RETRYABLE_NOT_COMMITTED—to KV_TXN_COMMIT_ERR. That source status explicitly means the FDB transaction did not commit, so this TSO can be abandoned directly. Because this branch cannot distinguish it from a timeout or unknown commit, it advances the global fence anyway, rejecting every in-flight TSO at or below this one with TXN_COMMIT_TSO_EXPIRED and potentially deactivating the whole TSO service if the extra fence RPC fails. Preserve the underlying status or fence only genuinely ambiguous commit results.
There was a problem hiding this comment.
The mechanism is right: MetaServiceProxy::call_impl rewrites KV_TXN_STORE_COMMIT_RETRYABLE to KV_TXN_COMMIT_ERR once the retries run out, and the last attempt then did not commit, so that fence is not needed.
I am keeping it:
- A fence is always safe, and here it costs what it costs after a failed RPC: one more MetaService RPC, and the commits in flight at or below the TSO retry once with a new TSO. It takes five attempts over at least 7.5 seconds of backoff, all failing with FDB throttling,
process_behind,database_lockedor the like, so commits are failing anyway by then. - The FE cannot tell this case apart. MetaService rewrites the answer from the last attempt only, and the same loop also retries
KV_TXN_MAYBE_COMMITTED, so an earlier attempt may have had an unknown result. Fencing only the ambiguous answers needs MetaService to report that across attempts, with a code clients can see. - Such a code changes the MetaService protocol: an older proto2 client may read an enum value it does not know as unset and fall back to
OK, which is why [fix](cloud) Hide KV_TXN_MAYBE_COMMITTED from clients #62244 keepsKV_TXN_MAYBE_COMMITTEDinside MetaService. A new FE talking to an older MetaService still getsKV_TXN_COMMIT_ERRand has to fence it. That belongs to a MetaService change, not to this FE fix.
Checking this also showed that the description listed the sources of KV_TXN_COMMIT_ERR too narrowly: since #62244 an unknown result reaches the FE as KV_TXN_COMMIT_ERR as well, so this PR fences it too. I have updated the description to list every source, including the one you point out, and the trade-off above.
TPC-H: Total hot run time: 28399 ms |
TPC-DS: Total hot run time: 151058 ms |
ClickBench: Total hot run time: 24.37 s |
FE UT Coverage ReportIncrement line coverage |
What problem does this PR solve?
Issue Number: None
Related PR: #67820, #69831
Problem Summary:
In short: in cloud mode, when MetaService answers a commit with
KV_TXN_COMMIT_ERR, the FE gives up the commit TSO at once, as for a commit that failed for good. MetaService gives that answer when the FDB commit of the transaction timed out, and such a commit may still be applied afterwards. The committed TSO watermark can then pass a commit that is yet to land, and a bounded@incrread admitted at that watermark misses its rows. This PR advances the MetaService TSO fence to the TSO before giving it up on this answer, as the FE already does when the commit RPC itself fails.Background
commit_txnto MetaService ([fix](binlog) Track committed TSO and fence uncertain commits for bounded incremental reads #67820). The committed TSO watermark stays below the oldest registered TSO, and a bounded@incrread whose window ends at or below the watermark is admitted without waiting (TSOService.waitForReadableWindow), on the guarantee that no commit with a TSO at or below it can still land.TSOService.fenceAndAbandonCommitTso):commit_txnreads the fence in the FDB transaction it commits and rejects a TSO at or below it, so once the fence is advanced, the commit has landed or can no longer land. Every other answer gives the TSO up at once.fdb_txn_timeout_ms, 10 seconds by default) and turns the error of an FDB commit into its answer throughcast_as<ErrCategory::COMMIT>: a conflict isKV_TXN_CONFLICT,commit_unknown_resultisKV_TXN_MAYBE_COMMITTED, an error FDB reports as retryable and not committed isKV_TXN_STORE_COMMIT_RETRYABLE, and a timeout (FDB errors 1004timed_outand 1031transaction_timed_out) or an error it does not classify isKV_TXN_COMMIT_ERR.MetaServiceProxy::call_implthen retries a conflict, an unknown result and a retryable error up to 4 times, and answers an unknown result or a retryable error withKV_TXN_COMMIT_ERRonce the retries run out. SoKV_TXN_MAYBE_COMMITTEDnever reaches the FE: since [fix](cloud) Hide KV_TXN_MAYBE_COMMITTED from clients #62244, MetaService answers it withKV_TXN_COMMIT_ERR, after the retries or at once when they are turned off.The problem, and what it cost
KV_TXN_COMMIT_ERRdoes not settle the outcome of the commit.CloudGlobalTransactionMgr.commitTxngives the TSO up on that answer without a fence. Once nothing older is registered, the watermark passes the TSO while the commit can still land.@incrread admitted at that watermark misses the rows of a commit that lands later, and a read of the next window does not see them either: their TSO lies in the window already read. Anything else that takes the watermark as proof that no commit at or below it is coming is exposed the same way; the keep-alive of IVM streams in [fix](ivm) Keep the offsets of idle IVM stream partitions alive #69831 fences MetaService itself before it reads, for this reason.How this PR fixes it
KV_TXN_COMMIT_ERRtakes the path of a failed RPC:fenceAndAbandonCommitTsoadvances the fence to the TSO and gives the TSO up only after that.KV_TXN_COMMIT_ERRin any case. The fence is safe there and costs what it costs after a failed RPC.Results
commit_txnKV_TXN_COMMIT_ERR(the FDB commit timed out or failed with an error MetaService does not classify, or the retries ran out on an unknown result or a retryable error)KV_TXN_TOO_OLD,KV_TXN_CONFLICT_RETRY_EXCEEDED_MAX_TIMESand the other errorsOK,TXN_ALREADY_VISIBLE,TXN_ALREADY_ABORTEDThe cost stays on that error path: one more MetaService RPC, and the commits in flight with a TSO at or below the fence are rejected once with
TXN_COMMIT_TSO_EXPIREDand retried with a new TSO, as after a failed RPC.Classes, and how they call each other
CloudGlobalTransactionMgr.commitTxn: the new branch forKV_TXN_COMMIT_ERR.CloudCommittedTsoTest: the newtestKvCommitErrorIsFencedButDefiniteFailureIsReleased.Not changed: MetaService, and the FE branch for
KV_TXN_MAYBE_COMMITTED, an answer MetaService has not sent since #62244.Release note
Fix a bounded incremental read (
@incr) in cloud mode that could miss a transaction whose commit timed out in MetaService.Check List (For Author)
Test
CloudCommittedTsoTest#testKvCommitErrorIsFencedButDefiniteFailureIsReleased:KV_TXN_COMMIT_ERRadvances the fence before the TSO is given up and still fails the commit; a fence that fails fails the commit with the fence error and gives nothing up;KV_TXN_TOO_OLDstill gives the TSO up without a fence. It fails on the code before this PR. The cloud transaction and TSO test classes (org/apache/doris/cloud/transaction/*Testandorg/apache/doris/tso/*Test, 7 classes, 133 tests) pass.Behavior changed:
A commit that MetaService answers with
KV_TXN_COMMIT_ERRcosts one more MetaService RPC, and the commits in flight at or below its TSO are retried once with a new TSO. If that fence fails, the TSO service deactivates until it is calibrated again, as after a failed commit RPC.Does this need documentation?
Check List (For Reviewer who merge this PR)