Class JsonTransaction
SimpleJsonDataOperator the transaction
has written, an overlay of staged puts and deletes, kept in first-touch order.
A write made inside the transaction is staged here instead of reaching the operator's
committed entries. The operator merges the overlay into every read the same transaction makes,
so the transaction sees its own writes; every other thread reads the committed entries only.
commit() first makes the transaction durable: it writes every staged change to one
record of the identity's journal (JsonJournal), forced to disk and renamed into place;
the rename is the commit point. It then applies every overlay to its operator's committed
entries while holding the identity's visibility write lock once, from before the first operator
to after the last, so another thread's read sees all of a commit's changes on every table or none
of them, and marks every changed key dirty for the next checkpoint. The identity's commit gate is
held, read side, from the record's sequence number to the end of the apply. A transaction that
staged nothing writes no record, forces nothing and takes no gate.
rollback() drops the overlays; it never touches committed entries, so it cannot undo a
write another caller made.
Before its first write to a table the transaction takes that table's write lock
(JsonLockTable) and holds it until it commits (after the apply) or rolls back, so no
other writer -- another transaction or a plain write -- can change the table in between. It
records every hold it takes and releases exactly those, so a transaction that runs while another
one on the same thread is suspended never releases the suspended one's holds.
Settings, applied by JsonTransactionManager right after the transaction begins:
- Lock at first read (
REPEATABLE_READ,SERIALIZABLE, and every transaction begun byDataOperator#transaction(...), question 11 A of the Phase 20.4 decisions): the first read of a table also takes its write lock, so the table stays still for the rest of the transaction and a read-modify-write cannot lose an update. The lock is requested before the read takes the visibility read side, never while holding it. - Read-only: every write fails with a
DataAccessExceptionbefore it locks or stages anything. - Deadline (
@Transactional(timeout)): every operation checks it first and fails once it has passed, marking the transaction rollback-only first (as Spring'sResourceHolderSupport#checkTransactionTimeoutdoes), and it bounds every lock wait (the sooner of it and the 5-second default bound).
A failed lock wait marks the transaction rollback-only (question 7 A of the Phase 20.4
decisions). A write, or a REPEATABLE_READ/SERIALIZABLE read, whose table lock
cannot be obtained -- the wait reached its bound or the transaction's deadline, it would close a
cycle, or the thread was interrupted -- marks this transaction rollback-only before the
DataAccessException reaches the caller. Catching the exception does not save the
transaction: its outermost commit discards every staged write and throws
UnexpectedRollbackException. The mark is the manager's own rollback-only flag, the one a
failed nested scope sets. The transaction keeps the table locks it already holds until it ends,
like any other open transaction; the failed request itself holds nothing. A refusal of the
statement itself -- a write in a read-only transaction, an id that cannot name its file, and the
operator's other argument checks -- does not mark the transaction, as a failed statement does
not end a MySQL or SQLite transaction.
Staged values are detached copies: no object the caller holds is reachable from an overlay.
An instance is confined to the thread whose JsonTransactionManager context holds it
(suspended contexts included), so it needs no synchronisation of its own.
Internal to the JSON backend. It lives in the operator's package so it can reach the
operator's package-private apply method; JsonTransactionManager creates and drives it.
- Since:
- 6.3.0
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static final classOne operator's staged writes, keyed by the key the operator's committed entries use.(package private) static final classOne staged write: a detached value to put, ornullfor a delete.(package private) static interfaceA test callback for the named steps of a commit. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate LongTheSystem.nanoTime()value at which the transaction's timeout passes, ornull.private final Map<SimpleJsonDataOperator<?>, JsonLockTable> The table locks this transaction holds, in the order it took them, with the lock table of each.private booleanWhether a first read of a table also takes its lock (REPEATABLE_READ,SERIALIZABLE).private final JsonTransactionManagerprivate static final JsonTransaction.StepHookprivate final Map<SimpleJsonDataOperator<?>, JsonTransaction.Overlay<?>> One overlay per operator this transaction has written, in first-touch order.private boolean(package private) static final StringFired after the whole apply, once the visibility write lock is released, before the commit gate and the table locks are.(package private) static final StringFired inside the visibility write lock, after one operator's overlay is applied and before the next.private static JsonTransaction.StepHookTest seam: called with a step name at the points named above. -
Constructor Summary
ConstructorsConstructorDescriptionJsonTransaction(JsonTransactionManager manager) Created byJsonTransactionManager.begin()for a new outermost transaction. -
Method Summary
Modifier and TypeMethodDescriptionprivate static <T extends BaseDataEntity<String>>
voidaddEntries(SimpleJsonDataOperator<?> operator, JsonTransaction.Overlay<?> overlay, List<JsonJournal.Entry> record) private static <T extends BaseDataEntity<String>>
voidapply(SimpleJsonDataOperator<?> operator, JsonTransaction.Overlay<?> overlay) private void(package private) voidbeforeRead(SimpleJsonDataOperator<?> operator) Called before a read ofoperatortakes the visibility read side: checks the deadline and, atREPEATABLE_READorSERIALIZABLE, takes the table's write lock.(package private) voidbeforeWrite(SimpleJsonDataOperator<?> operator) Called before a write evaluates or stages anything, never while holding either side of the visibility lock: rejects it in a read-only transaction, checks the deadline, and takesoperator's table write lock for the rest of this transaction unless it already holds it.private List<JsonJournal.Entry> The journal entries of every staged write, operators in first-touch order and each operator's writes in staging order.private voidcheckDeadline(SimpleJsonDataOperator<?> operator) Refuses the operation once the deadline has passed, marking this transaction rollback-only first, as Spring'sResourceHolderSupport#checkTransactionTimeoutcallssetRollbackOnly()before it throwsTransactionTimedOutException.voidcommit()Commits: with the identity's commit gate held (read side), allocates a sequence number, writes the journal record of every staged change, forced and renamed into place (the commit point), then applies every overlay to its operator's committed entries under the visibility write lock held once across all operators and marks each changed key dirty.The deadline, as aSystem.nanoTime()value, ornullif none is set.private boolean(package private) booleanhasStagedWrites(SimpleJsonDataOperator<?> operator) Whether this transaction has staged any write foroperator.(package private) static voidprivate voidlock(SimpleJsonDataOperator<?> operator) Takesoperator's table lock unless this transaction holds it, waiting at most until the sooner bound.private voidMarks this transaction rollback-only, for a failed lock wait or an expired deadline, through the manager's own mechanism, the wayTransactionInterceptorrecords a failed participant: the failed request joins the transaction as one more scope and rolls that scope back, and a rollback at a nested depth sets the manager's rollback-only flag (D-08) without ending the transaction or releasing its locks.(package private) <T extends BaseDataEntity<String>>
JsonTransaction.Overlay<T> overlay(SimpleJsonDataOperator<T> operator) The overlay this transaction keeps foroperator, ornullif the transaction has not written it.(package private) <T extends BaseDataEntity<String>>
JsonTransaction.Overlay<T> overlayForWrite(SimpleJsonDataOperator<T> operator) The overlay foroperator, created (in first-touch order) if this is its first write.private voidReleases every table lock this transaction took, most recent first.(package private) static voidvoidrollback()Discards every staged write and releases this transaction's table locks.voidsetDeadlineNanos(Long deadlineNanos) Sets the deadline.voidsetLockAtFirstRead(boolean lockAtFirstRead) Sets whether a table's first read also takes its write lock.voidsetReadOnly(boolean readOnly) Sets whether writes are rejected.private static voidwriteRecord(JsonIdentity identity, List<JsonJournal.Entry> record) Allocates the record's number and writes the record; its rename is the commit point.
-
Field Details
-
STEP_APPLY_BETWEEN_OPERATORS
Fired inside the visibility write lock, after one operator's overlay is applied and before the next.- See Also:
-
STEP_APPLIED
Fired after the whole apply, once the visibility write lock is released, before the commit gate and the table locks are.- See Also:
-
NO_STEP_HOOK
-
stepHook
Test seam: called with a step name at the points named above. A no-op in production; a test installs a callback that pauses or fails at one step and resets it afterwards. -
manager
-
overlays
One overlay per operator this transaction has written, in first-touch order. -
heldLocks
The table locks this transaction holds, in the order it took them, with the lock table of each. -
lockAtFirstRead
private boolean lockAtFirstReadWhether a first read of a table also takes its lock (REPEATABLE_READ,SERIALIZABLE). -
readOnly
private boolean readOnly -
deadlineNanos
TheSystem.nanoTime()value at which the transaction's timeout passes, ornull.
-
-
Constructor Details
-
JsonTransaction
Created byJsonTransactionManager.begin()for a new outermost transaction.- Parameters:
manager- the manager whose context holds this transaction and whose visibility lock the commit publishes under
-
-
Method Details
-
overlay
<T extends BaseDataEntity<String>> JsonTransaction.Overlay<T> overlay(SimpleJsonDataOperator<T> operator) The overlay this transaction keeps foroperator, ornullif the transaction has not written it. -
overlayForWrite
<T extends BaseDataEntity<String>> JsonTransaction.Overlay<T> overlayForWrite(SimpleJsonDataOperator<T> operator) The overlay foroperator, created (in first-touch order) if this is its first write. -
setLockAtFirstRead
public void setLockAtFirstRead(boolean lockAtFirstRead) Sets whether a table's first read also takes its write lock. Called byJsonTransactionManager.setIsolationLevel(int); not part of the supported API.- Parameters:
lockAtFirstRead-trueforREPEATABLE_READandSERIALIZABLE
-
setReadOnly
public void setReadOnly(boolean readOnly) Sets whether writes are rejected. Called byJsonTransactionManager.setReadOnly(boolean); not part of the supported API.- Parameters:
readOnly-trueto reject every write of this transaction
-
setDeadlineNanos
Sets the deadline. Called byJsonTransactionManager.setTimeout(int); not part of the supported API.- Parameters:
deadlineNanos- aSystem.nanoTime()value, ornullfor none
-
getDeadlineNanos
The deadline, as aSystem.nanoTime()value, ornullif none is set. Not part of the supported API.- Returns:
- the deadline or
null
-
beforeRead
Called before a read ofoperatortakes the visibility read side: checks the deadline and, atREPEATABLE_READorSERIALIZABLE, takes the table's write lock.- Throws:
DataAccessException- if the deadline has passed, or the lock wait fails; either first marks this transaction rollback-only
-
beforeWrite
Called before a write evaluates or stages anything, never while holding either side of the visibility lock: rejects it in a read-only transaction, checks the deadline, and takesoperator's table write lock for the rest of this transaction unless it already holds it.- Throws:
DataAccessException- if the transaction is read-only, the deadline has passed, or the lock wait fails (deadlock or lock-wait timeout); an expired deadline and a failed lock wait first mark this transaction rollback-only, so catching the exception does not save it, and its locks are released when it ends; the read-only refusal does not
-
checkDeadline
Refuses the operation once the deadline has passed, marking this transaction rollback-only first, as Spring'sResourceHolderSupport#checkTransactionTimeoutcallssetRollbackOnly()before it throwsTransactionTimedOutException. -
lock
Takesoperator's table lock unless this transaction holds it, waiting at most until the sooner bound. A wait that fails marks this transaction rollback-only before the exception propagates. -
markRollbackOnly
private void markRollbackOnly()Marks this transaction rollback-only, for a failed lock wait or an expired deadline, through the manager's own mechanism, the wayTransactionInterceptorrecords a failed participant: the failed request joins the transaction as one more scope and rolls that scope back, and a rollback at a nested depth sets the manager's rollback-only flag (D-08) without ending the transaction or releasing its locks. Called only frombeforeRead(com.ultikits.ultitools.interfaces.impl.data.json.SimpleJsonDataOperator<?>)andbeforeWrite(com.ultikits.ultitools.interfaces.impl.data.json.SimpleJsonDataOperator<?>), which an operator calls on its manager's current transaction, so the manager's active context is this transaction's. -
hasStagedWrites
Whether this transaction has staged any write foroperator. -
commit
public void commit()Commits: with the identity's commit gate held (read side), allocates a sequence number, writes the journal record of every staged change, forced and renamed into place (the commit point), then applies every overlay to its operator's committed entries under the visibility write lock held once across all operators and marks each changed key dirty. Releases the gate, then the table locks. The overlays are discarded and the table locks released whether or not the commit succeeded. A transaction that staged nothing writes no record and takes neither the gate nor the visibility lock.- Throws:
DataAccessException- (TRANSACTION_FAILED) if the journal record could not be written; nothing was committed, and the transaction is rolled back
-
hasAnyStagedWrite
private boolean hasAnyStagedWrite() -
buildRecord
The journal entries of every staged write, operators in first-touch order and each operator's writes in staging order.- Throws:
DataAccessException- (TRANSACTION_FAILED) if a staged key cannot name an entity file (it is not one path segment); nothing was committed
-
addEntries
private static <T extends BaseDataEntity<String>> void addEntries(SimpleJsonDataOperator<?> operator, JsonTransaction.Overlay<?> overlay, List<JsonJournal.Entry> record) -
writeRecord
Allocates the record's number and writes the record; its rename is the commit point. -
rollback
public void rollback()Discards every staged write and releases this transaction's table locks. Committed entries are not touched. -
applyOverlays
private void applyOverlays() -
releaseLocks
private void releaseLocks()Releases every table lock this transaction took, most recent first. A release that fails does not stop the others; the first failure is rethrown afterwards with the rest suppressed. -
apply
private static <T extends BaseDataEntity<String>> void apply(SimpleJsonDataOperator<?> operator, JsonTransaction.Overlay<?> overlay) -
installStepHook
-
resetStepHook
static void resetStepHook()
-