Surfaced by the #694 review's domain pass; the behavior is pre-existing (preserved verbatim from the legacy libuv commit path) — #694 did not change it.
In the async-commit execute path, store->commitFinished(...) (which advances the committed-read watermark and makes the log entry visible, e.g. to replication) is gated on committedPosition.logSequenceNumber > 0 && !status.IsBusy() — not on status.ok(). If the txn-log writeBatch succeeded but txn->Commit() then fails with a hard non-Busy error (Aborted/IOError), the log entry is still finalized: the transaction log marks an entry committed/visible whose data never committed to RocksDB locally. A replication source could ship an entry it never applied.
Note the interaction with the abort path: TransactionHandle::close() calls store->commitAborted(committedPosition) for non-Committed transactions, which may compensate — the actual visibility window/semantics between commitFinished at execute time and commitAborted at close time needs tracing before changing the gate to status.ok() (the #668 IsBusy-retry hasLog semantics also touch this).
🤖 Generated with Claude Code
Surfaced by the #694 review's domain pass; the behavior is pre-existing (preserved verbatim from the legacy libuv commit path) — #694 did not change it.
In the async-commit execute path,
store->commitFinished(...)(which advances the committed-read watermark and makes the log entry visible, e.g. to replication) is gated oncommittedPosition.logSequenceNumber > 0 && !status.IsBusy()— not onstatus.ok(). If the txn-logwriteBatchsucceeded buttxn->Commit()then fails with a hard non-Busy error (Aborted/IOError), the log entry is still finalized: the transaction log marks an entry committed/visible whose data never committed to RocksDB locally. A replication source could ship an entry it never applied.Note the interaction with the abort path:
TransactionHandle::close()callsstore->commitAborted(committedPosition)for non-Committed transactions, which may compensate — the actual visibility window/semantics betweencommitFinishedat execute time andcommitAbortedat close time needs tracing before changing the gate tostatus.ok()(the #668 IsBusy-retry hasLog semantics also touch this).🤖 Generated with Claude Code