Interface Transaction

All Superinterfaces:
AutoCloseable, TransactionContext

@NonExtendable public interface Transaction extends AutoCloseable, TransactionContext
A global operation where participants guarantee atomicity: either the whole operation succeeds, or it is completely aborted and rolled back.

One can imagine that transactions are like video game checkpoints.

  • Opening a transaction with a try-with-resources block creates a checkpoint.
  • Modifications to game state can then happen.
  • Calling commit() validates the modifications that happened during the transaction, essentially discarding the checkpoint.
  • Calling abort() or doing nothing and letting the transaction be closed at the end of the try-with-resources block cancels any modification that happened during the transaction, reverting to the checkpoint.
  • Calling openNested(TransactionContext) on a transaction creates a new nested transaction, i.e. a new checkpoint with the current state. Committing a nested transaction will validate the changes that happened, but they may still be cancelled later if a parent transaction is cancelled. Aborting a nested transaction immediately reverts the changes - cancelling any modification made after the call to openNested(TransactionContext).

This is illustrated in the following example.

try (Transaction outerTransaction = Transaction.openOuter()) {
    // (A) some transaction operations
    try (Transaction nestedTransaction = outerTransaction.openNested()) {
        // (B) more operations
        nestedTransaction.commit(); // Validate the changes that happened in this transaction.
                                    // This is a nested transaction, so changes will only be applied if the outer
                                    // transaction is committed too.
    }
    // (C) even more operations
    outerTransaction.commit(); // This is an outer transaction: changes (A), (B) and (C) are applied.
}
// If we hadn't committed the outerTransaction, all changes (A), (B) and (C) would have been reverted.

Participants are responsible for upholding this contract themselves, by using TransactionContext.addCloseCallback(TransactionContext.CloseCallback) to react to transaction close events and properly validate or revert changes. Any action that modifies state outside of the transaction, such as calls to setChanged() or neighbor updates, should be deferred until after the outer transaction is closed to give every participant a chance to react to transaction close events.

This is very low-level for most applications, and most participants should subclass SnapshotParticipant that will take care of properly maintaining their state.

Participants should generally be passed a TransactionContext parameter instead of the full Transaction, to make sure they don't call abort(), commit() or close() mistakenly.

Every transaction is only valid on the thread it was opened on, and attempts to use it on another thread will throw an exception. Consequently, transactions can be concurrent across multiple threads, as long as they don't share any state.

  • Method Details

    • openOuter

      static Transaction openOuter()
      Open a new outer transaction.
      Throws:
      IllegalStateException - If a transaction is already active on the current thread.
    • isOpen

      static boolean isOpen()
      Returns:
      True if a transaction is open or closing on the current thread, and false otherwise.
    • getLifecycle

      static Transaction.Lifecycle getLifecycle()
      Returns:
      The current lifecycle of the transaction stack on this thread.
    • openNested

      static Transaction openNested(@Nullable TransactionContext maybeParent)
      Open a nested transaction if maybeParent is non-null, or an outer transaction if maybeParent is null.
    • getCurrentUnsafe

      @Deprecated static @Nullable TransactionContext getCurrentUnsafe()
      Deprecated.
      Only use if you absolutely need it, there is almost always a better way.
      Retrieve the currently open transaction, or null if there is none.

      Usage of this function is strongly discouraged, this is why it is deprecated and contains unsafe in its name. The transaction may be aborted unbeknownst to you and anything you think that you have committed might be undone. Only use it if you have no way to pass the transaction down the stack, for example if you are implementing compat with a simulation-based API, and you know what you are doing, for example because you opened the outer transaction.

      Throws:
      IllegalStateException - If called from a close or outer close callback.
    • abort

      void abort()
      Close the current transaction, rolling back all the changes that happened during this transaction and the transactions opened with openNested(TransactionContext) from this transaction.
      Throws:
      IllegalStateException - If this function is not called on the thread this transaction was opened in.
      IllegalStateException - If this transaction is not the current transaction.
      IllegalStateException - If this transaction was closed.
    • commit

      void commit()
      Close the current transaction, committing all the changes that happened during this transaction and the committed transactions opened with openNested(TransactionContext) from this transaction. If this transaction was opened with openOuter(), all changes are applied. If this transaction was opened with openNested(TransactionContext), all changes will be applied when and if the changes of the parent transactions are applied.
      Throws:
      IllegalStateException - If this function is not called on the thread this transaction was opened in.
      IllegalStateException - If this transaction is not the current transaction.
      IllegalStateException - If this transaction was closed.
    • close

      void close()
      Abort the current transaction if it was not closed already.
      Specified by:
      close in interface AutoCloseable