org.multiverse.api
Interface Transaction

All Known Implementing Classes:
AbstractTransaction

public interface Transaction

All changes on transaction objects must be done through a Transaction. The transaction make sure that changes on java objects are:

Thread-safety
A Transaction is not thread-safe to use (just like a Hibernate Session is not thread-safe to use). It can be handed over from thread to thread, but one needs to be really careful with threadlocals. Although the Stm/Transaction implementation don't care about threadlocals, the stuff in front (templates, instrumented code etc) could depend on threadlocals.

TransactionLifecycle
It is possible to listen to a Transaction when it aborts, or commits. This can be done with the registerLifecycleListener(TransactionLifecycleListener).

When the Transaction is reset, the lifecycle tasks are dropped. So the lifecycle task need to be registered again.

Blocking
It is possible to let a transaction block until some state changes has happened; comparable with the waitset/Condition functionality already provided. For more information see the registerRetryLatch(Latch).

Because a custom Latch implementation can be provided, you have a lot of control on the blocking behavior. But atm it is not possible to get fairness on when the Latch is opened. Policies to customize starvation, lifelocking, deadlocking will be added in the future.

Author:
Peter Veentjer.

Method Summary
 void abort()
          Aborts this Transaction.
 void commit()
          Commits this Transaction.
 int getAttempt()
          Gets the current attempt (so the number of tries this transaction already had).
 TransactionConfiguration getConfiguration()
          Gets the TransactionConfiguration that is used by this Transaction.
 long getReadVersion()
          Returns the clock version of the stm when this Transaction started.
 long getRemainingTimeoutNs()
          Gets the remaining timeout in nanoseconds.
 TransactionStatus getStatus()
          Returns the status of this Transaction.
 Stm getStm()
          Returns the Stm that started this Transaction.
 TransactionFactory getTransactionFactory()
          Returns the TransactionFactory that started this Transaction.
 void prepare()
          Prepares this transaction to be committed.
 void registerLifecycleListener(TransactionLifecycleListener listener)
          Registers a TransactionLifecycleListener on this Transaction.
 void registerRetryLatch(Latch latch)
          Registers the retry Latch on this Transaction.
 void reset()
          Resets this Transaction so it can be reused.
 void setAttempt(int attempt)
          Sets the current attempt.
 void setRemainingTimeoutNs(long timeoutNs)
          Sets the remaining timeout in nanoseconds.
 void start()
          Starts the Transaction.
 

Method Detail

getTransactionFactory

TransactionFactory getTransactionFactory()
Returns the TransactionFactory that started this Transaction.

Returns:
the TransactionFactory that started this Transaction.

getStm

Stm getStm()
Returns the Stm that started this Transaction.

Returns:
the Stm that started this Transaction.

getConfiguration

TransactionConfiguration getConfiguration()
Gets the TransactionConfiguration that is used by this Transaction.

Returns:
the used TransactionConfiguration.

getReadVersion

long getReadVersion()
Returns the clock version of the stm when this Transaction started. This version is needed to provide a transaction level read consistent view (so a transaction will always see a stable view of the objects at some point in time).

The value is unspecified once the Transaction is aborted or committed.

Method depends on long as time, and is going to be removed or replaced in the future.

Returns:
the version of the stm when this Transaction started.

getStatus

TransactionStatus getStatus()
Returns the status of this Transaction.

Returns:
the status of this Transaction.

getAttempt

int getAttempt()
Gets the current attempt (so the number of tries this transaction already had). Value will always be equal or larger than 0.

Returns:
the current attempt.

setAttempt

void setAttempt(int attempt)
Sets the current attempt.

This normally isn't called from the user code, it is the task of the stm internals and the transaction management to use the attempt.

Parameters:
attempt - the current attempt
Throws:
IllegalArgumentException - if attempt smaller than zero.

getRemainingTimeoutNs

long getRemainingTimeoutNs()
Gets the remaining timeout in nanoseconds. Long.MAX_VALUE indicates that no timeout should be used.

Returns:
the remaining timeout.

setRemainingTimeoutNs

void setRemainingTimeoutNs(long timeoutNs)
Sets the remaining timeout in nanoseconds. Long.MAX_VALUE indicates that no timeout should be used.

This normally isn't called from the user code, it is task of the stm internals and the transaction management to use the timeout.

Parameters:
timeoutNs - the timeout.
Throws:
IllegalArgumentException - if timeout smaller than 0.

start

void start()
Starts the Transaction. Transaction normally started as New.

If a clock based stm is used (like the AlphaStm), when the transaction is New, the readversion is not set. When it is started, it is set. Based on this readversion the transaction is able to construct a read consistent view. But the sooner the read version is set, the higher the chance that a transaction runs out of history to read from.

Throws:
IllegalTransactionStateException - if the Transaction is not in the correct state for this operation.

commit

void commit()
Commits this Transaction. If the Transaction is:
  1. new: is is started and following the same flow as an active transaction. This is done so that the lifecyclelisteners work.
  2. active: it is prepared for commit and then committed
  3. prepared: it is committed (so changes persisted)
  4. aborted: a DeadTransactionException is thrown
  5. committed: a DeadTransactionException is thrown
So it is safe to call while active or prepared.

Transaction will be aborted if the commit does not succeed.

Commit will not throw any validation exceptions after the transaction is prepared.

Throws:
WriteConflict - if the commit failed. Check the class hierarchy of the WriteConflict for more information.
DeadTransactionException - if this transaction already is aborted.

prepare

void prepare()
Prepares this transaction to be committed. It can lock resources to make sure that no conflicting changes are made after the transaction has been prepared. If the transaction already is prepared, the call is ignored. If the prepare fails, the transaction automatically is aborted.

It is very important that the transaction eventually commits or aborts, if it doesn't no other transaction reading/writing the committed resources, can't commit.

Throws:
WriteConflict - if the transaction can't be prepared.
DeadTransactionException - if the transaction already is committed or aborted.

abort

void abort()
Aborts this Transaction. This means that the changes made in this transaction are not committed. It depends on the implementation if this operation is simple (ditching objects for example), or if changes need to be rolled back. If an exception is thrown while executing the abort, the transaction is still aborted. And example of such a situation is a pre-abort task that fails. So the transaction always is aborted (unless it is committed).

If the Transaction already is aborted, the call is ignored.

Throws:
DeadTransactionException - if this transaction already is committed

reset

void reset()
Resets this Transaction so it can be reused. After the reset is executed the Transaction is back to the TransactionStatus.New state.

It doesn't matter what the transaction state of the transaction is. This is the preferred way to reuse a transaction (e.g. when a ControlFlowError or when an old transaction on the ThreadLocalTransaction is found that can be reused.

If the Transaction is prepared or committed, it will be aborted before it is reset. If there are TransactionLifecycleListeners that cause problems while executing the pre/post abort notification, the transaction will be aborted and the exception will be propagated.


registerRetryLatch

void registerRetryLatch(Latch latch)
Registers the retry Latch on this Transaction. This functionality is required for the retry mechanism (so blocking!) and is something different than 'just' restarting. The Latch contains all the 'waiting' logic, so you can do timed and non interruptible timeouts on that structure. A latch can be compared to a Future because it also pops closed once some event occurs (an interesting write in the case of a Latch).

Parameters:
latch - the Latch to register.
Throws:
NullPointerException - if latch is null.
NoRetryPossibleException - if the retry can't make progress, e.g. because the transaction has not loaded any object.
DeadTransactionException - if this transaction already is committed or aborted.
UnsupportedOperationException - if the Transaction doesn't support this operation (for example because it doesn't do readtracking).

registerLifecycleListener

void registerLifecycleListener(TransactionLifecycleListener listener)
Registers a TransactionLifecycleListener on this Transaction. It can be used to receive a callback from the transaction if the TransactionStatus of that transaction. Without this functionality it would not be possible to execute compensating or deferred action once a transaction aborts or commits.

If the execution of one of the listeners fails, the others won't be executed. If one of the listeners fails before the commit/abort, the transaction is aborted (no matter what).

The listeners are executed in the order they are registered.

If the same listener is added multiple times, it will be notified multiple times.

The listener will be executed on the thread that starts the commit/abort.

If the listener is added after the Transaction is prepared, the preCommit event will not be called on the listener.

If the listener accesses the stm after the transaction has been committed or aborted, it could see changes made after that transaction. So all assumptions about state are possibly wrong so one needs to take care of re-validating state if needed.

A good use case of this feature is starting up threads. If you need to start threads, you don't want to start them immediately because eventually the transaction could be aborted.

The registration is 'transactional', so when the transaction is restarted, the listeners need to be registered again. In most cases this is the behavior wanted because listeners are added inside the atomic 'block' and this block is re-executed when a failure occurs.

Parameters:
listener - the TransactionLifecycleListener to registerLifecycleListener
Throws:
NullPointerException - if listener is null.
DeadTransactionException - if transaction already is aborted or committed.


Copyright © 2008-2010 Multiverse. All Rights Reserved.