Add a Transaction view that scopes reads and writes to a single ACID transaction - #165
Open
jtnelson wants to merge 3 commits into
Open
Add a Transaction view that scopes reads and writes to a single ACID transaction#165jtnelson wants to merge 3 commits into
jtnelson wants to merge 3 commits into
Conversation
jtnelson
force-pushed
the
feature/transactions
branch
from
August 6, 2026 22:10
17028f8 to
b189545
Compare
jtnelson
force-pushed
the
feature/transactions
branch
from
August 6, 2026 22:55
b189545 to
f7ca0ad
Compare
…action Runway#transaction starts a Transaction: a DatabaseInterface view whose reads join the transaction's conflict footprint and whose bound Records save within it, so a commit fails instead of persisting a decision that was made on data a concurrent writer changed. An Audience loaded through the view routes its operations through the transaction, and an ended transaction forwards to the enclosing Runway so bound records unwind to the database scope. A Record now binds to a single PersistentDatabaseInterface and draws connections through a ConcourseProvider; it never starts or commits a transaction itself.
jtnelson
force-pushed
the
feature/transactions
branch
from
August 6, 2026 23:04
f7ca0ad to
90b204a
Compare
A DeferredReference now follows its owner's binding at the moment of the first access, so an access inside an open Transaction loads within the snapshot, joins the conflict footprint, and binds the loaded Record to the transaction; an access after the transaction ends falls through to the enclosing Runway.
…uting Runway#run executes a Consumer of DatabaseInterface within a transaction: the work reads through the provided view, and every record.save() during the work, including a save of a brand-new Record, routes into the ambient transaction, so arbitrary work commits together or not at all.
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.
Summary
Runway#transactionstarts aTransaction: aDatabaseInterfaceview whose reads and writes all resolve within a single ACID transaction. It exists so a decision made on loaded data can commit only if that data is still current, which closes the read-then-write race that unguarded saves leave open.Behavior
record.save()stages within the transaction, and the writes become durable only whencommit()succeeds. New records join throughTransaction#save.Audienceloaded through the view routes the operations it performs through the transaction, so access-controlled reads and writes stay within the snapshot and join the conflict footprint.TransactionisAutoCloseable(close()aborts whatever was not committed) and is confined to the thread that starts it. After it ends, the view forwards reads and saves to the enclosingRunway, so bound records unwind to the database scope; only anothercommit()is refused.runway.run(work)executes work within a transaction and commits after it completes: the work reads through the providedDatabaseInterface, everyrecord.save()during the work joins the transaction, new records included, and conflicts retry within the bounds of the governingAtomicRetryPolicy, so work must be free of side effects outside the transaction.Runwaybinding and are refused on a transaction-bound record; the transaction's commit is the unit of atomicity.DeferredReferencethat is first accessed within a transaction resolves within it: the lazy load joins the snapshot and the conflict footprint, and the loaded record binds to the transaction.Scope
Recordbinds to a single persistence-capable database view that reads and saves route through, and draws connections through the newConcourseProviderseam;Recordnever starts or commits a transaction itself.