org.multiverse.api.Txn Maven / Gradle / Ivy
package org.multiverse.api;
import org.multiverse.api.lifecycle.TxnListener;
/**
* The unit of work for {@link Stm}. The transaction make sure that changes on {@link TxnObject} instances are:
*
* - Atomic: all or nothing gets committed (Failure atomicity)
* - Consistent :
* - Isolated: a transaction is executed isolated from other transactions. Meaning that a transaction won't see changed made by
* transactions executed concurrently, but it will see changes made by transaction completed before. It depends on the
* {@link IsolationLevel} or {@link LockMode} used how strict the isolation is.
*
*
* Thread-safety
*
* A Txn is not thread-safe (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 the {@link TxnThreadLocal} or other
* thread specific state like the stackframe of a method (this is an issue when instrumentation is used since the stackframe
* is likely to be enhanced to include the Txn as a local variable.
*
*
TxnListener
*
* It is possible to listen to a Txn when it aborts/prepares/commits/starts. There are 2 different flavors of
* listeners:
*
* - normal listeners: are registered during the execution of a transaction using the
* {@link Txn#register(org.multiverse.api.lifecycle.TxnListener)} method. If the transactions aborts/commits
* these listeners are removed. So if the transaction is retried, the listeners need to be registered (this is easy since
* the logic inside the transactional closure that did the register, is executed again.
*
* - permanent listeners: are registered once and will always remain. It can be done on the
* TxnExecutor level using the {@link TxnFactoryBuilder#addPermanentListener(org.multiverse.api.lifecycle.TxnListener)}
* or it can be done on the Stm level. Permanent listeners are suited for products that want to integrate with Multiverse and always
* execute some logic at important transaction events. Registration of permanent can also be done on the {@link Stm} level. See
* the implementations for more details. Permanent listeners are always executed after the normal listeners.
*
*
*
* Storing transaction references
*
* Txn instances should not be stored since they are likely to be pooled by the STM. So it could be that the same
* transaction instance is re-used to execute a completely unrelated piece of logic, and it can also be that different instances
* are used to execute the same logic.
*
* @author Peter Veentjer.
*/
public interface Txn {
/**
* Returns the TxnConfig used by this Txn.
*
*
Because the Txn can be reused, the TxnConfig used by this Txn doesn't need to be constant.
*
* @return the TxnConfig.
*/
TxnConfig getConfig();
/**
* Returns the status of this Txn.
*
* @return the status of this Txn.
*/
TxnStatus getStatus();
/**
* Gets the current attempt (so the number of tries this transaction already had). Value will
* always be equal or larger than 1 (the first attempt returns 1). The maximum number of attempts for retrying is determined based
* on the {@link TxnConfig#getMaxRetries()}
*
* @return the current attempt.
*/
int getAttempt();
/**
* Gets the remaining timeout in nanoseconds. Long.MAX_VALUE indicates that no timeout is used.
*
*
The remaining timeout only is decreased if a transaction blocks on a retry or when doing
* a backoff.
*
* @return the remaining timeout.
*/
long getRemainingTimeoutNs();
/**
* Commits this Txn. If the Txn is:
*
* - active: it is prepared for commit and then committed
* - prepared: it is committed. Once it is prepared, the commit is guaranteed to
* succeed.
* - aborted: a DeadTxnException is thrown
* - committed: the call is ignored
*
*
* Txn will always be aborted if the commit does not succeed.
*
*
Commit will not throw a {@link org.multiverse.api.exceptions.ReadWriteConflict} after the transaction is prepared.
* So if prepared successfully, a commit will always succeed.
*
*
If there are TxnListeners (either normal ones or permanent ones) and they thrown a {@link RuntimeException}
* or {@link Error}, this will be re-thrown. If a listener fails after the prepare/commit the transaction still is
* committed.
*
* @throws org.multiverse.api.exceptions.ReadWriteConflict
* if the commit failed. Check the class hierarchy of the ReadWriteConflict for more information.
* @throws org.multiverse.api.exceptions.IllegalTxnStateException
* if the Txn is not in the correct
* state for this operation.
*/
void commit();
/**
* 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. Once a transaction is prepared, the commit will
* always succeed.
*
*
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 org.multiverse.api.exceptions.ReadWriteConflict
* if the transaction can't be prepared.
* @throws org.multiverse.api.exceptions.DeadTxnException
* if the transaction already is committed or aborted.
*/
void prepare();
/**
* Aborts this Txn. 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 Txn already is aborted, the call is ignored.
*
* @throws org.multiverse.api.exceptions.IllegalTxnStateException
* if the Txn is not in the correct state for this operation.
*/
void abort();
/**
* Retries the transaction. This call doesn't block, but if all goes well a {@link org.multiverse.api.exceptions.RetryError}
* is thrown which is caught by the {@link TxnExecutor}.
*
* @throws org.multiverse.api.exceptions.TxnExecutionException
* if the transaction is not in a legal state for
* this operation.
* @throws org.multiverse.api.exceptions.ControlFlowError
*
*/
void retry();
/**
* Signals that the only possible outcome of the Txn is one that aborts. When the transaction prepares or
* commits it checks if the transaction is marked as abort only. If so, it will automatically aborted and an
* {@link org.multiverse.api.exceptions.AbortOnlyException} is thrown.
*
*
This method is not threadsafe, so can only be called by the thread that used the transaction.
*
* @throws org.multiverse.api.exceptions.IllegalTxnStateException
* if the transaction is not active.
* @throws org.multiverse.api.exceptions.ControlFlowError
*
*/
void setAbortOnly();
/**
* Checks if this Txn is abort only (so will always fail when committing or preparing).
*
*
This method is not threadsafe, so can only be called by the thread that used the transaction.
*
* @return true if abort only, false otherwise.
* @throws org.multiverse.api.exceptions.DeadTxnException
* if the transaction is committed/aborted.
*/
boolean isAbortOnly();
/**
* Registers a TxnListener. Every time a transaction is retried, the listener needs to
* be registered again if you want the task to be executed again. If you want a permanent listener, have
* a look at the {@link TxnFactoryBuilder#addPermanentListener(org.multiverse.api.lifecycle.TxnListener)}.
*
*
If a TxnListener is added more than once, it is executed more than once. No checks
* are made. The permanent listeners are executed in the order they are added.
*
*
If a TxnListener throws an Error/RuntimeException and the transaction still is alive,
* it is aborted. For compensating and deferred actions this is not an issue, but for the PrePrepare state
* or the state it could since the transaction is aborted.
*
* @param listener the listener to add.
* @throws NullPointerException if listener is null. If the transaction is still alive, it is aborted.
* @throws org.multiverse.api.exceptions.IllegalTxnStateException
* if the transaction is not in the correct
* state (e.g. aborted or committed).
* @throws org.multiverse.api.exceptions.ControlFlowError
*
*/
void register(TxnListener listener);
}