aboutsummaryrefslogtreecommitdiff
path: root/internal/persistence/txn.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-16 13:52:58 -0400
committerhistoria <[not public]>2026-09-16 13:52:58 -0400
commitc054b27f628f23fecc505b9e09e78721738f3c4e (patch)
treeea197165bc454ee811dd22b9ceef5dc94bb0b279 /internal/persistence/txn.go
parentf2d44f10f52d3fe68fdb19b518a330f7af9e1ed2 (diff)
downloadthehouseoficarus-c054b27f628f23fecc505b9e09e78721738f3c4e.tar.gz
fix: shutdown admission, recovery identity/order, rollback journal retention, pickup merge
Diffstat (limited to 'internal/persistence/txn.go')
-rw-r--r--internal/persistence/txn.go13
1 files changed, 12 insertions, 1 deletions
diff --git a/internal/persistence/txn.go b/internal/persistence/txn.go
index 7d86cc8..93d186a 100644
--- a/internal/persistence/txn.go
+++ b/internal/persistence/txn.go
@@ -11,6 +11,11 @@ package persistence
// Transactions do NOT serialize concurrent callers: like single-file saves,
// they must be executed by an owner that prevents concurrent mutations of
// the same files (the game's owner goroutine).
+//
+// On apply failure the applied operations are rolled back and the journal is
+// removed. If the ROLLBACK itself fails, the journal is deliberately kept:
+// its before-images are then the only durable record of the pre-transaction
+// state, and the next Recover retries the rollback.
import (
"crypto/rand"
@@ -184,7 +189,13 @@ func (t *Transaction) Commit() error {
if err := t.apply(); err != nil {
if rbErr := t.rollback(); rbErr != nil {
- err = fmt.Errorf("%v; rollback failed: %v", err, rbErr)
+ // The rollback could not restore the before-images: the journal
+ // is now the only durable record of the pre-transaction state.
+ // KEEP it so the next Recover call (or operator repair of the
+ // underlying fault) can retry the rollback instead of silently
+ // losing the before-images forever. See
+ // TestCommitKeepsJournalAfterFailedRollback.
+ return fmt.Errorf("%v; rollback failed (journal kept for recovery: %s): %v", err, journalPath, rbErr)
}
if rErr := os.Remove(journalPath); rErr != nil && !os.IsNotExist(rErr) {
err = fmt.Errorf("%v; journal cleanup failed: %v", err, rErr)