Repository navigation
Conversation
…applied TransactionManager::commit makes a transaction's COMMIT record durable before publishCommit() applies it. When publish failed (a full buffer pool, or a named graph dropped by a concurrent DROP GRAPH), the caller rolled the transaction back in memory while the WAL kept the record, so a crash before the next checkpoint made recovery apply a COMMIT the client saw fail; a retried primary-key insert then left a database that no longer opened. - After such a failure, append and sync an ABORT record (new WAL record type) naming the transaction by the WAL offset where its records start, then report the original error. If the ABORT cannot be written either, poison the WAL: writes are refused until a reopen, which applies the transaction. - Recovery's dry pass collects aborted transactions; the replay loops of the active, frozen and graph WALs skip their records. - Remove the unused Transaction::commit(WAL*), a second publish path without this handling; tools/wal_dump prints the new record; docs describe it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 24, 2026
This branch has not been deployed
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.
This work was produced with the help of language models.
Fixes #1377.
A
COMMITthat failed withBuffer manager exception: Unable to allocate memory! The buffer pool is full and no memory could be freed!after its WAL commit record was durable was rolled back in memory, but a crash before the next checkpoint made recovery apply it. #1377's repro reopened with 187 nodes where the connection saw 186, and with the failed primary-key insert retried, reopening failed withFound duplicated primary key value 186, which violates the uniqueness constraint of the primary key column.. A clean close or a checkpoint before the crash dropped the transaction instead.Fix. When applying a transaction fails after its
COMMITrecord is durable,TransactionManager::commitappends and syncs anABORTrecord that names the transaction by the WAL offset where its records start, then reports the original error. Recovery's dry pass collects the aborted transactions, and the replay loops of the active WAL, the frozen WAL and graph WALs skip their records. A failedCOMMITis therefore never applied, and the database stays writable. If theABORTrecord cannot be written either, the WAL refuses writes until a reopen, which then applies the transaction; the error says so. A transaction's records are appended as one block, and a checkpoint waits until it is rolled back, so itsCOMMITandABORTland in the same file even with several writers. A crash between the two leaves the transaction applied, as a crash during anyCOMMITmay. The unusedTransaction::commit(WAL*), a second publish path without this handling, is removed, andtools/wal_dumpprints the new record.Compatibility.
ABORTis a new WAL record type. An older build treats it as a corrupt tail: it stops replay there and applies the transaction it undoes, or refuses to open withthrowOnWalReplayFailure. The record lasts only until the next checkpoint retires the WAL;docs/checkpoint_recovery.mddescribes it and lists it with the other records older builds cannot read.Tests. In
FailedCommitTest(test/transaction/transaction_test.cpp),FailedCommitIsNotAppliedAfterCrash,RetriedPrimaryKeyInsertReopensAfterCrashandFailedCommitOfTwoTablesLeavesNeithercrash after aCOMMITthat fails while being applied;mainreopens with the failed relationship, fails on the duplicated key, and keeps the failed node.AbortRecordSkipsOnlyTheNamedTransaction,AbortRecordSkipsTheTransactionInAFrozenWAL,TornAbortRecordLeavesTheTransactionandAbortRecordNamingNoTransactionIsIgnoredappend anABORTto a WAL: naming the middle one of three transactions, in a WAL renamed to the frozen WAL, torn, and naming no transaction.FailedAbortLeavesTheTransactionToRecoverymakes theABORTfail too, andWalTest.AbortSyncFailurePoisonsWALfails its sync. The skip in graph-WAL replay has no test, since it needs a failing commit in a standalone session on the graph's file.KeepsCheckpointedRels(#1195) andConcurrentDropGraphDuringStagedInsertCommitFailsCleanlypass unchanged. Full ctest: 3190/3190; storage-compat suite, every release: same results asmain.Touches the same code as #1378 (
WALReplayer::dryReplayand the replay loops); merging the two keeps both changes, and that merge is what the stress run in #1376 used.