JBoss.orgCommunity Documentation
Atomic actions (transactions) can be used by both application programmers and class developers. Thus entire operations (or parts of operations) can be made atomic as required by the semantics of a particular operation. This chapter will describe some of the more subtle issues involved with using transactions in general and TxCore in particular.
In some cases it may be necessary to enlist participants that are not two-phase commit aware into a two-phase commit transaction. If there is only a single resource then there is no need for two-phase commit. However, if there are multiple resources in the transaction, the Last Resource Commit optimization (LRCO) comes into play. It is possible for a single resource that is one-phase aware (i.e., can only commit or roll back, with no prepare), to be enlisted in a transaction with two-phase commit aware resources. The coordinator treats the one-phase aware resource slightly differently, in that it executes the prepare phase on all other resource first, and if it then intends to commit the transaction it passes control to the one-phase aware resource. If it commits, then the coordinator logs the decision to commit and attempts to commit the other resources as well.
In order to utilize the LRCO, your participant must implement the
com.arjuna.ats.arjuna.coordinator.OnePhase
interface and be registered with the
transaction through the BasicAction.add
operation. Since this operation expects instances
of AbstractRecord
, you must create an instance of
com.arjuna.ats.arjuna.LastResourceRecord
and give your participant as the constructor
parameter.
Example 4.1. Class com.arjuna.ats.arjuna.LastResourceRecord
try
{
boolean success = false;
AtomicAction A = new AtomicAction();
OnePhase opRes = new OnePhase(); // used OnePhase interface
System.err.println("Starting top-level action.");
A.begin();
A.add(new LastResourceRecord(opRes));
A.add(new ShutdownRecord(ShutdownRecord.FAIL_IN_PREPARE));
A.commit();
}
There are no special constructs for nesting of transactions. If an action is begun while another action is running then it is automatically nested. This allows for a modular structure to applications, whereby objects can be implemented using atomic actions within their operations without the application programmer having to worry about the applications which use them, and whether or not the applications will use atomic actions as well. Thus, in some applications actions may be top-level, whereas in others they may be nested. Objects written in this way can then be shared between application programmers, and TxCore will guarantee their consistency.
If a nested action is aborted, all of its work will be undone, although strict two-phase locking means that any locks it may have obtained will be retained until the top-level action commits or aborts. If a nested action commits then the work it has performed will only be committed by the system if the top-level action commits. If the top-level action aborts then all of the work will be undone.
The committing or aborting of a nested action does not automatically affect the outcome of the action within which it is nested. This is application dependent, and allows a programmer to structure atomic actions to contain faults, undo work, etc.
By default, the Transaction Service executes the commit
protocol of a top-level
transaction in a synchronous manner. All registered resources will be told to prepare in order by a single thread,
and then they will be told to commit or rollback. This has several possible disadvantages:
In the case of many registered resources, the prepare
operating can logically be
invoked in parallel on each resource. The disadvantage is that if an “early” resource in the list of
registered resource forces a rollback during prepare
, possibly many prepare
operations will have been made needlessly.
In the case where heuristic reporting is not required by the application, the second phase of the commit protocol can be done asynchronously, since its success or failure is not important.
Therefore, JBoss Transaction Service provides runtime options to enable possible threading optimizations. By
setting the CoordinatorEnvironmentBean.asyncPrepare
environment variable to
YES
, during the prepare
phase a separate thread will be created for
each registered participant within the transaction. By setting
CoordinatorEnvironmentBean.asyncCommit
to YES
, a separate thread will be
created to complete the second phase of the transaction if knowledge about heuristics outcomes is not required.
In addition to normal top-level and nested atomic actions, TxCore also supports independent top-level actions, which can be used to relax strict serializability in a controlled manner. An independent top-level action can be executed from anywhere within another atomic action and behaves exactly like a normal top-level action. Its results are made permanent when it commits and will not be undone if any of the actions within which it was originally nested abort.
Top-level actions can be used within an application by declaring and using instances of the class
TopLevelTransaction
. They are used in exactly the same way as other transactions.
Exercise caution when writing the save_state
and restore_state
operations to ensure that no atomic actions are started, either explicitly in the operation or implicitly through
use of some other operation. This restriction arises due to the fact that TxCore may invoke
restore_state
as part of its commit processing resulting in the attempt to execute an
atomic action during the commit or abort phase of another action. This might violate the atomicity properties of
the action being committed or aborted and is thus discouraged.
Example 4.2.
If we consider the Example 3.7, “Array Class” given previously, the set
and
get
operations could be implemented as shown below.
This is a simplification of the code, ignoring error conditions and exceptions.
public boolean set (int index, int value)
{
boolean result = false;
AtomicAction A = new AtomicAction();
A.begin();
// We need to set a WRITE lock as we want to modify the state.
if (setlock(new Lock(LockMode.WRITE), 0) == LockResult.GRANTED)
{
elements[index] = value;
if ((value > 0) &&(index > highestIndex
highestIndex = index;
A.commit(true);
result = true;
}
else
A.rollback();
return result;
}
public int get (int index) // assume -1 means error
{
AtomicAction A = new AtomicAction();
A.begin();
// We only need a READ lock as the state is unchanged.
if (setlock(new Lock(LockMode.READ), 0) == LockResult.GRANTED)
{
A.commit(true);
return elements[index];
}
else
A.rollback();
return -1;
}
Java objects are deleted when the garbage collector determines that they are no longer required. Deleting an object that is currently under the control of a transaction must be approached with caution since if the object is being manipulated within a transaction its fate is effectively determined by the transaction. Therefore, regardless of the references to a transactional object maintained by an application, TxCore will always retain its own references to ensure that the object is not garbage collected until after any transaction has terminated.
By default, transactions live until they are terminated by the application that created them or a failure occurs. However, it is possible to set a timeout (in seconds) on a per-transaction basis such that if the transaction has not terminated before the timeout expires it will be automatically rolled back.
In TxCore, the timeout value is provided as a parameter to the AtomicAction
constructor. If a value of AtomicAction.NO_TIMEOUT
is provided (the default) then the
transaction will not be automatically timed out. Any other positive value is assumed to be the timeout for the
transaction (in seconds). A value of zero is taken to be a global default timeout, which can be provided by the
property CoordinatorEnvironmentBean.defaultTimeout
, which has a default value of 60 seconds.
Default timeout values for other JBoss Transaction Service components, such as JTS, may be different and you should consult the relevant documentation to be sure.
When a top-level transaction is created with a non-zero timeout, it is subject to being rolled back if it has not completed within the specified number of seconds. JBoss Transaction Service uses a separate reaper thread which monitors all locally created transactions, and forces them to roll back if their timeouts elapse. If the transaction cannot be rolled back at that point, the reaper will force it into a rollback-only state so that it will eventually be rolled back.
By default this thread is dynamically scheduled to awake according to the timeout values for any transactions
created, ensuring the most timely termination of transactions. It may alternatively be configured to awake at a
fixed interval, which can reduce overhead at the cost of less accurate rollback timing. For periodic operation,
change the CoordinatorEnvironmentBean.txReaperMode
property from its default value of
DYNAMIC
to PERIODIC
and set the interval between runs, in milliseconds,
using the property CoordinatorEnvironmentBean.txReaperTimeout
. The default interval in
PERIODIC
mode is 120000 milliseconds.
In earlier versions the PERIODIC
mode was known as NORMAL
and was the
default behavior. The use of the configuration value NORMAL
is deprecated and
PERIODIC
should be used instead if the old scheduling behavior is still required.
If a value of 0
is specified for the timeout of a top-level transaction, or no timeout is
specified, then JBoss Transaction Service will not impose any timeout on the transaction, and the transaction will
be allowed to run indefinitely. This default timeout can be overridden by setting the
CoordinatorEnvironmentBean.defaultTimeout
property variable when using to the required timeout
value in seconds, when using ArjunaCore, ArjunaJTA or ArjunaJTS.
As of JBoss Transaction Service 4.5, transaction timeouts have been unified across all transaction components and are controlled by ArjunaCore.
If you want to be informed when a transaction is rolled back or forced into a rollback-only mode by the reaper,
you can create a class that inherits from class
com.arjuna.ats.arjuna.coordinator.listener.ReaperMonitor
and overrides the
rolledBack
and markedRollbackOnly
methods. When registered
with the reaper via the TransactionReaper.addListener
method, the reaper will invoke
one of these methods depending upon how it tries to terminate the transaction.
The reaper will not inform you if the transaction is terminated (committed or rolled back) outside of its control, such as by the application.