Summary
When a commit fails with a write-write conflict, the transaction stays active in the transaction manager. Every later transaction that writes the same entity then fails with a write-write conflict too, until the process restarts.
Reproduction
import grafeo
db = grafeo.GrafeoDB()
db.execute("INSERT (:Person {name: 'Alix', age: 30})")
ta = db.begin_transaction()
tb = db.begin_transaction()
tb.execute("MATCH (n:Person {name: 'Alix'}) SET n.age = 31")
tb.commit()
ta.execute("MATCH (n:Person {name: 'Alix'}) SET n.age = 32")
try:
ta.commit() # fails with a write-write conflict, as expected
except Exception as e:
print("expected conflict:", e)
tc = db.begin_transaction()
tc.execute("MATCH (n:Person {name: 'Alix'}) SET n.age = 33") # also fails, and keeps failing until restart
Cause
Session::commit_inner handles the conflict by replaying the property undo log and clearing pending CDC events, but it never calls TransactionManager::abort, does not discard the versions the transaction created and writes no WAL abort record. The only production call to abort is in the rollback path. The transaction stays Active, so:
record_write keeps reporting a conflict against it for every later writer of the same entities,
active_count stays above zero, which disables the single-writer fast path,
min_active_epoch stays pinned, so version garbage collection stops reclaiming newer versions.
Expected
A failed commit aborts the transaction completely: its entities are released, its versions discarded, an abort is logged and the error is returned to the caller.
Summary
When a commit fails with a write-write conflict, the transaction stays active in the transaction manager. Every later transaction that writes the same entity then fails with a write-write conflict too, until the process restarts.
Reproduction
Cause
Session::commit_innerhandles the conflict by replaying the property undo log and clearing pending CDC events, but it never callsTransactionManager::abort, does not discard the versions the transaction created and writes no WAL abort record. The only production call toabortis in the rollback path. The transaction staysActive, so:record_writekeeps reporting a conflict against it for every later writer of the same entities,active_countstays above zero, which disables the single-writer fast path,min_active_epochstays pinned, so version garbage collection stops reclaiming newer versions.Expected
A failed commit aborts the transaction completely: its entities are released, its versions discarded, an abort is logged and the error is returned to the caller.