[id='transaction_manager']
= Transactions
{brandname} can be configured to use and to participate in JTA compliant transactions.

Alternatively, if transaction support is disabled, it is equivalent to using autocommit in JDBC calls, where modifications are potentially replicated after every change (if replication is enabled).

On every cache operation {brandname} does the following:

. Retrieves the current {tx} associated with the thread
. If not already done, registers {xa} with the transaction manager to be notified when a transaction commits or is rolled back.

In order to do this, the cache has to be provided with a reference to the environment's {tm}.
This is usually done by configuring the cache with the class name of an implementation of the {tm-lookup} interface.
When the cache starts, it will create an instance of this class and invoke its `getTransactionManager()` method, which returns a reference to the `TransactionManager`.

{brandname} ships with several transaction manager lookup classes:

.Transaction manager lookup implementations
*  {embedded-tm-lookup}:
This provides with a basic transaction manager which should only be used for embedded mode when no other implementation is available.
This implementation has some severe limitations to do with concurrent transactions and recovery.

* {jboss-tm-lookup}:
If you're running {brandname} in a standalone environment, or in JBoss AS 7 and earlier, and WildFly 8, 9, and 10, this should be your default choice for transaction manager.
It's a fully fledged transaction manager based on link:http://narayana.io/[JBoss Transactions] which overcomes all the deficiencies of the `EmbeddedTransactionManager`.

* {wildfly-tm-lookup}:
If you're running {brandname} in WildFly 11 or later, this should be your default choice for transaction manager.

* {generic-tm-lookup}:
This is a lookup class that locate transaction managers in the most popular Java EE application servers.
If no transaction manager can be found, it defaults on the `EmbeddedTransactionManager`.

Once initialized, the `TransactionManager` can also be obtained from the `Cache` itself:

[source,java]
----
//the cache must have a transactionManagerLookupClass defined
Cache cache = cacheManager.getCache();

//equivalent with calling TransactionManagerLookup.getTransactionManager();
TransactionManager tm = cache.getAdvancedCache().getTransactionManager();
----

[[tx_configuration]]
== Configuring transactions
Transactions are configured at cache level.
Below is the configuration that affects a transaction behaviour and a small description of each configuration attribute.

[source,xml,options="nowrap",subs=attributes+]
----
include::xml/transaction_config.xml[]
----

or programmatically:

[source,java]
----
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.locking()
    .isolationLevel(IsolationLevel.READ_COMMITTED);
builder.transaction()
    .lockingMode(LockingMode.OPTIMISTIC)
    .autoCommit(true)
    .completedTxTimeout(60000)
    .transactionMode(TransactionMode.NON_TRANSACTIONAL)
    .useSynchronization(false)
    .notifications(true)
    .reaperWakeUpInterval(30000)
    .cacheStopTimeout(30000)
    .transactionManagerLookup(new GenericTransactionManagerLookup())
    .recovery()
    .enabled(false)
    .recoveryInfoCacheName("__recoveryInfoCacheName__");
----


* `isolation` - configures the isolation level. Check section link:#tx_isolation_levels[Isolation Levels] for more details.
Default is `REPEATABLE_READ`.
* `locking` - configures whether the cache uses optimistic or pessimistic locking. Check section link:#tx_locking[Transaction Locking] for more details.
Default is `OPTIMISTIC`.
* `auto-commit` - if enable, the user does not need to start a transaction manually for a single operation. The transaction is automatically started and committed.
 Default is `true`.
* `complete-timeout` - the duration in milliseconds to keep information about completed transactions. Default is `60000`.
* `mode` - configures whether the cache is transactional or not. Default is `NONE`. The available options are:
** `NONE` - non transactional cache
** `FULL_XA` - XA transactional cache with recovery enabled. Check section link:#tx_recovery[Transaction recovery] for more details about recovery.
** `NON_DURABLE_XA` - XA transactional cache with recovery disabled.
** `NON_XA` - transactional cache with integration via {sync} instead of XA.
Check section link:#tx_sync_enlist[Enlisting Synchronizations] for details.
** `BATCH`-  transactional cache using batch to group operations. Check section link:#tx_batching[Batching] for details.
* `notifications` - enables/disables triggering transactional events in cache listeners. Default is `true`.
* `reaper-interval` - the time interval in millisecond at which the thread that cleans up transaction completion information kicks in.
Defaults is `30000`.
* `recovery-cache` - configures the cache name to store the recovery information. Check section link:#tx_recovery[Transaction recovery] for more details about recovery.
Default is `__recoveryInfoCacheName__`.
* `stop-timeout` - the time in millisecond to wait for ongoing transaction when the cache is stopping. Default is  `30000`.
* `transaction-manager-lookup` - configures the fully qualified class name of a class that looks up a reference to a `jakarta.transaction.TransactionManager`.
Default is `org.infinispan.transaction.lookup.GenericTransactionManagerLookup`.


For more details on how Two-Phase-Commit (2PC) is implemented in {brandname} and how locks are being acquired see the section below.
More details about the configuration settings are available in link:{configdocroot}[Configuration reference].

[[tx_isolation_levels]]
== Isolation levels
{brandname} offers two isolation levels - link:https://en.wikipedia.org/wiki/Isolation_(database_systems)#Read_committed[READ_COMMITTED] and link:https://en.wikipedia.org/wiki/Isolation_(database_systems)#Repeatable_reads[REPEATABLE_READ].

These isolation levels determine when readers see a concurrent write, and are internally implemented using different subclasses of `MVCCEntry`, which have different behaviour in how state is committed back to the data container.

Here's a more detailed example that should help understand the difference between `READ_COMMITTED` and `REPEATABLE_READ` in the context of {brandname}.
With `READ_COMMITTED`, if between two consecutive read calls on the same key, the key has been updated by another transaction, the second read may return the new updated value:

[source,java]
----
Thread1: tx1.begin()
Thread1: cache.get(k) // returns v
Thread2:                                       tx2.begin()
Thread2:                                       cache.get(k) // returns v
Thread2:                                       cache.put(k, v2)
Thread2:                                       tx2.commit()
Thread1: cache.get(k) // returns v2!
Thread1: tx1.commit()
----

With `REPEATABLE_READ`, the final get will still return `v`.
So, if you're going to retrieve the same key multiple times within a transaction, you should use `REPEATABLE_READ`.

However, as read-locks are not acquired even for `REPEATABLE_READ`, this phenomena can occur:

[source,java]
----
cache.get("A") // returns 1
cache.get("B") // returns 1

Thread1: tx1.begin()
Thread1: cache.put("A", 2)
Thread1: cache.put("B", 2)
Thread2:                                       tx2.begin()
Thread2:                                       cache.get("A") // returns 1
Thread1: tx1.commit()
Thread2:                                       cache.get("B") // returns 2
Thread2:                                       tx2.commit()
----

[[tx_locking]]
== Transaction locking

=== Pessimistic transactional cache

From a lock acquisition perspective, pessimistic transactions obtain locks on keys at the time the key is written.

. A lock request is sent to the primary owner (can be an explicit lock request or an operation)
. The primary owner tries to acquire the lock:
.. If it succeed, it sends back a positive reply;
.. Otherwise, a negative reply is sent and the transaction is rollback.

As an example:

[source,java]
----
transactionManager.begin();
cache.put(k1,v1); //k1 is locked.
cache.remove(k2); //k2 is locked when this returns
transactionManager.commit();
----

When `cache.put(k1,v1)` returns, `k1` is locked and no other transaction running anywhere in the cluster can write to it.
Reading `k1` is still possible.
The lock on `k1` is released when the transaction completes (commits or rollbacks).

NOTE: For conditional operations, the validation is performed in the originator.

=== Optimistic transactional cache

With optimistic transactions locks are being acquired at transaction prepare time and are only being held up to the point the transaction commits (or rollbacks).
This is different from the 5.0 default locking model where local locks are being acquire on writes and cluster locks are being acquired during prepare time.

. The prepare is sent to all the owners.
. The primary owners try to acquire the locks needed:
.. If locking succeeds, it performs the write skew check.
.. If the write skew check succeeds (or is disabled), send a positive reply.
.. Otherwise, a negative reply is sent and the transaction is rolled back.

As an example:

[source,java]
----
transactionManager.begin();
cache.put(k1,v1);
cache.remove(k2);
transactionManager.commit(); //at prepare time, K1 and K2 is locked until committed/rolled back.
----

NOTE: For conditional commands, the validation still happens on the originator.

=== What do I need - pessimistic or optimistic transactions?
From a use case perspective, optimistic transactions should be used when there is _not_ a lot of contention between multiple transactions running at the same time.
That is because the optimistic transactions rollback if data has changed between the time it was read and the time it was committed (with write skew check enabled).

On the other hand, pessimistic transactions might be a better fit when there is high contention on the keys and transaction rollbacks are less desirable.
Pessimistic transactions are more costly by their nature: each write operation potentially involves a RPC for lock acquisition.

[[tx_write_skew]]
== Write Skews

Write skews occur when two transactions independently and simultaneously read and write to the same key. The result of a write skew is that both transactions successfully commit updates to the same key but with different values.

{brandname} automatically performs write skew checks to ensure data consistency for `REPEATABLE_READ` isolation levels in optimistic transactions. This allows {brandname} to detect and roll back one of the transactions.

When operating in `LOCAL` mode, write skew checks rely on Java object
references to compare differences, which provides a reliable technique for
checking for write skews.

=== Forcing write locks on keys in pessimitic transactions

To avoid write skews with pessimistic transactions, lock keys at read-time with `Flag.FORCE_WRITE_LOCK`.

[NOTE]
====
* In non-transactional caches, `Flag.FORCE_WRITE_LOCK` does not work. The `get()` call reads the key value but does not acquire locks remotely.

* You should use `Flag.FORCE_WRITE_LOCK` with transactions in which the entity is updated later in the same transaction.
====

Compare the following code snippets for an example of `Flag.FORCE_WRITE_LOCK`:

[source, java]
----
// begin the transaction
if (!cache.getAdvancedCache().lock(key)) {
   // abort the transaction because the key was not locked
} else {
   cache.get(key);
   cache.put(key, value);
   // commit the transaction
}
----

[source, java]
----
// begin the transaction
try {
   // throws an exception if the key is not locked.
   cache.getAdvancedCache().withFlags(Flag.FORCE_WRITE_LOCK).get(key);
   cache.put(key, value);
} catch (CacheException e) {
   // mark the transaction rollback-only
}
// commit or rollback the transaction
----

== Dealing with exceptions
If a link:{javadocroot}/org/infinispan/commons/CacheException.html[CacheException] (or a subclass of it) is thrown by a cache method within the scope of a JTA transaction, then the transaction is automatically marked for rollback.

[[tx_sync_enlist]]
== Enlisting Synchronizations
By default {brandname} registers itself as a first class participant in distributed transactions through {xa}.
There are situations where {brandname} is not required to be a participant in the transaction, but only to be notified by its lifecycle (prepare, complete): e.g. in the case {brandname} is used as a 2nd level cache in Hibernate.

{brandname} allows transaction enlistment through {sync}.
To enable it just use `NON_XA` transaction mode.

``Synchronization``s have the advantage that they allow `TransactionManager` to optimize 2PC with a 1PC where only one other resource is enlisted with that transaction (link:https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.0/html/development_guide/java_transaction_api_jta#about_the_lrco_optimization_for_single_phase_commit_1pc[last resource commit optimization]).
E.g. Hibernate second level cache: if {brandname} registers itself with the `TransactionManager` as a `XAResource` than at commit time, the `TransactionManager` sees two `XAResource` (cache and database) and does not make this optimization.
Having to coordinate between two resources it needs to write the tx log to disk.
On the other hand, registering {brandname} as a `Synchronization` makes the `TransactionManager` skip writing the log to the disk (performance improvement).

[[tx_batching]]
==  Batching
Batching allows atomicity and some characteristics of a transaction, but not full-blown JTA or XA capabilities.
Batching is often a lot lighter and cheaper than a full-blown transaction.

TIP: Generally speaking, one should use batching API whenever the only participant in the transaction is an {brandname} cluster.
On the other hand, JTA transactions (involving `TransactionManager`) should be used whenever the transactions involves multiple systems.
E.g. considering the "Hello world!" of transactions: transferring money from one bank account to the other.
If both accounts are stored within {brandname}, then batching can be used.
If one account is in a database and the other is {brandname}, then distributed transactions are required.

NOTE: You _do not_ have to have a transaction manager defined to use batching.

=== API
Once you have configured your cache to use batching, you use it by calling `startBatch()` and `endBatch()` on `Cache`. E.g.,

[source,java]
----
Cache cache = cacheManager.getCache();
// not using a batch
cache.put("key", "value"); // will replicate immediately

// using a batch
cache.startBatch();
cache.put("k1", "value");
cache.put("k2", "value");
cache.put("k2", "value");
cache.endBatch(true); // This will now replicate the modifications since the batch was started.

// a new batch
cache.startBatch();
cache.put("k1", "value");
cache.put("k2", "value");
cache.put("k3", "value");
cache.endBatch(false); // This will "discard" changes made in the batch
----

=== Batching and JTA
Behind the scenes, the batching functionality starts a JTA transaction, and all the invocations in that scope are associated with it.
For this it uses a very simple (e.g. no recovery) internal `TransactionManager` implementation.
With batching, you get:

. Locks you acquire during an invocation are held until the batch completes
. Changes are all replicated around the cluster in a batch as part of the batch completion process. Reduces replication chatter for each update in the batch.
. If synchronous replication or invalidation are used, a failure in replication/invalidation will cause the batch to roll back.
. All the transaction related configurations apply for batching as well.

[[tx_recovery]]
==  Transaction recovery
Recovery is a feature of XA transactions, which deal with the eventuality of a resource or possibly even the transaction manager failing, and recovering accordingly from such a situation.

=== When to use recovery
Consider a distributed transaction in which money is transferred from an account stored in an external database to an account stored in {brandname}.
When `TransactionManager.commit()` is invoked, both resources prepare successfully (1st phase). During the commit (2nd) phase, the database successfully applies the changes whilst {brandname} fails before receiving the commit request from the transaction manager.
At this point the system is in an inconsistent state: money is taken from the account in the external database but not visible yet in {brandname} (since locks are only released during 2nd phase of a two-phase commit protocol).
Recovery deals with this situation to make sure data in both the database and {brandname} ends up in a consistent state.

=== How does it work
Recovery is coordinated by the transaction manager.
The transaction manager works with {brandname} to determine the list of in-doubt transactions that require manual intervention and informs the system administrator (via email, log alerts, etc).
This process is transaction manager specific, but generally requires some configuration on the transaction manager.  

Knowing the in-doubt transaction ids, the system administrator can now connect to the {brandname} cluster and replay the commit of transactions or force the rollback.
{brandname} provides JMX tooling for this - this is explained extensively in the link:#tx_recovery_reconciliation[Transaction recovery and reconciliation] section.

=== Configuring recovery   
Recovery is _not_ enabled by default in {brandname}.
If disabled, the `TransactionManager` won't be able to work with {brandname} to determine the in-doubt transactions.
The link:#tx_configuration[Transaction configuration] section shows how to enable it.

NOTE: `recovery-cache` attribute is not mandatory and it is configured per-cache.

NOTE: For recovery to work, `mode` must be set to `FULL_XA`, since full-blown XA transactions are needed.

==== Enable JMX support

In order to be able to use JMX for managing recovery JMX support must be explicitly enabled.

=== Recovery cache
In order to track in-doubt transactions and be able to reply them, {brandname} caches all transaction state for future use.
This state is held only for in-doubt transaction, being removed for successfully completed transactions after when the commit/rollback phase completed.

This in-doubt transaction data is held within a local cache: this allows one to configure swapping this info to disk through cache loader in the case it gets too big.
This cache can be specified through the `recovery-cache` configuration attribute.
If not specified {brandname} will configure a local cache for you.

It is possible (though not mandated) to share same recovery cache between all the {brandname} caches that have recovery enabled.
If the default recovery cache is overridden, then the specified recovery cache must use a {tm-lookup} that returns a different transaction manager than the one used by the cache itself.

=== Integration with the transaction manager
Even though this is transaction manager specific, generally a transaction manager would need a reference to a `XAResource` implementation in order to invoke `XAResource.recover()` on it.
In order to obtain a reference to an {brandname} `XAResource` following API can be used:

[source,java]
----
XAResource xar = cache.getAdvancedCache().getXAResource();
----

It is a common practice to run the recovery in a different process from the one running the transaction.
//
//At the moment it is not possible to do this with {brandname} the recovery
//must be run from the same process where the {brandname} instance exists.
//This limitation will be dropped once
//link:https://issues.jboss.org/browse/ISPN-375[transactions over Hot Rod] are
//available.

[[tx_recovery_reconciliation]]
=== Reconciliation
The transaction manager informs the system administrator on in-doubt transaction in a proprietary way.
At this stage it is assumed that the system administrator knows transaction's XID (a byte array).

A normal recovery flow is:

* *STEP 1*: The system administrator connects to an {brandname} server through JMX, and lists the in doubt transactions.
The image below demonstrates JConsole connecting to an {brandname} node that has an in doubt transaction.

image::showInDoubtTx.png[align="center", title="Show in-doubt transactions"]

The status of each in-doubt transaction is displayed(in this example " _PREPARED_ ").
There might be multiple elements in the status field, e.g. "PREPARED" and "COMMITTED" in the case the transaction committed on certain nodes but not on all of them.  

* *STEP 2*: The system administrator visually maps the XID received from the transaction manager to an {brandname} internal id, represented as a number.
This step is needed because the XID, a byte array, cannot conveniently be passed to the JMX tool (e.g. JConsole) and then re-assembled on {brandname}'s side.

* *STEP 3*: The system administrator forces the transaction's commit/rollback through the corresponding jmx operation, based on the internal id.
The image below is obtained by forcing the commit of the transaction based on its internal id.

image::forceCommit.png[align="center", title="Force commit"]

TIP: All JMX operations described above can be executed on any node, regardless of where the transaction originated.

==== Force commit/rollback based on XID
XID-based JMX operations for forcing in-doubt transactions' commit/rollback are available as well: these methods receive byte[] arrays describing the XID instead of the number associated with the transactions (as previously described at step 2).
These can be useful e.g. if one wants to set up an automatic completion job for certain in-doubt transactions.
This process is plugged into transaction manager's recovery and has access to the transaction manager's XID objects.
