[id='hotrod_transactions']
= Hot Rod Java Client Transactions
You can configure and use Hot Rod clients in JTA {tx}s.

To participate in a transaction, the Hot Rod client requires the {tm} with which it interacts and whether it participates in the transaction through the {sync} or {xa} interface.

[IMPORTANT]
====
Transactions are optimistic in that clients acquire write locks on entries during the prepare phase.
To avoid data inconsistency, be sure to read about link:#hr_transactions_force_return_value[Detecting Conflicts with Transactions].
====

[[hr_transactions_config_server]]
== Configuring the Server
Caches in the server must also be transactional for clients to participate in JTA {tx}s.

The following server configuration is required, otherwise transactions rollback only:

* Isolation level must be `REPEATABLE_READ`.
* `PESSIMISTIC` locking mode is recommended but `OPTIMISTIC` can be used.
* Transaction mode should be `NON_XA` or `NON_DURABLE_XA`.
Hot Rod transactions should not use `FULL_XA` because it degrades performance.

For example:

[source,xml,options="nowrap",subs=attributes+]
----
<replicated-cache name="hotrodReplTx">
  <locking isolation="REPEATABLE_READ"/>
  <transaction mode="NON_XA" locking="PESSIMISTIC"/>
</replicated-cache>
----

Hot Rod transactions have their own recovery mechanism.

[[hr_transactions_config_client]]
== Configuring Hot Rod Clients

Transactional {rc} are configured per-cache basis.
The exception is the transaction's `timeout` which is global, because a single transaction can interact with multiple {rc}s.

The following example shows how to configure a transactional {rc} for cache `my-cache`:

[source,java]
----
org.infinispan.client.hotrod.configuration.ConfigurationBuilder cb = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder();
//other client configuration parameters
cb.transactionTimeout(1, TimeUnit.MINUTES);
cb.remoteCache("my-cache")
   .transactionManagerLookup(GenericTransactionManagerLookup.getInstance())
   .transactionMode(TransactionMode.NON_XA);
----

See {cb} and {rccb} Javadoc for documentation on configuration parameters.

You can also configure the Java Hot Rod client with a properties file, as in the following example:

[source]
----
infinispan.client.hotrod.cache.my-cache.transaction.transaction_manager_lookup = org.infinispan.client.hotrod.transaction.lookup.GenericTransactionManagerLookup
infinispan.client.hotrod.cache.my-cache.transaction.transaction_mode = NON_XA
infinispan.client.hotrod.transaction.timeout = 60000
----

[[hr_transactions_tmlookup]]
=== TransactionManagerLookup Interface

`TransactionManagerLookup` provides an entry point to fetch a {tm}.

Available implementations of `TransactionManagerLookup`:

{gtml}::
A lookup class that locates {tm}s running in Java EE application servers.
Defaults to the {rtm} if it cannot find a {tm}.
This is the default for Hot Rod Java clients.

[TIP]
====
In most cases, {gtml} is suitable.
However, you can implement the `TransactionManagerLookup` interface if you need to integrate a custom {tm}.
====

{rtml}::
A basic, and volatile, {tm} if no other implementation is available.
Note that this implementation has significant limitations when handling concurrent transactions and recovery.

[[hr_transactions_modes]]
== Transaction Modes

{tx-mode} controls how a {rc} interacts with the {tm}.

[IMPORTANT]
====
Configure transaction modes on both the {brandname} server and your client application.
If clients attempt to perform transactional operations on non-transactional caches, runtime exceptions can occur.
====

Transaction modes are the same in both the {brandname} configuration and client settings.
Use the following modes with your client, see the {brandname} configuration schema for the server:

`NONE`::
The {rc} does not interact with the {tm}.
This is the default mode and is non-transactional.

`NON_XA`::
The {rc} interacts with the {tm} via {sync}.

`NON_DURABLE_XA`::
The {rc} interacts with the {tm} via {xa}.
Recovery capabilities are disabled.

`FULL_XA`::
The {rc} interacts with the {tm} via {xa}.
Recovery capabilities are enabled.
Invoke the `XaResource.recover()` method to retrieve transactions to recover.

[[hr_transactions_force_return_value]]
== Detecting Conflicts with Transactions

Transactions use the initial values of keys to detect conflicts.

For example, "k" has a value of "v" when a transaction begins.
During the prepare phase, the transaction fetches "k" from the server to read the value.
If the value has changed, the transaction rolls back to avoid a conflict.

[NOTE]
====
Transactions use versions to detect changes instead of checking value equality.
====

The `forceReturnValue` parameter controls write operations to the {rc} and helps avoid conflicts.
It has the following values:

* If `true`, the {tm} fetches the most recent value from the server before performing write operations.
However, the `forceReturnValue` parameter applies only to write operations that access the key for the first time.

* If `false`, the {tm} does not fetch the most recent value from the server before performing write operations.

[NOTE]
====
This parameter does not affect _conditional_ write operations such as `replace` or `putIfAbsent`
because they require the most recent value.
====

The following transactions provide an example where the `forceReturnValue` parameter can prevent conflicting write operations:

.Transaction 1 (TX1)
[source,java]
----
RemoteCache<String, String> cache = ...
TransactionManager tm = ...

tm.begin();
cache.put("k", "v1");
tm.commit();
----

.Transaction 2 (TX2)
[source,java]
----
RemoteCache<String, String> cache = ...
TransactionManager tm = ...

tm.begin();
cache.put("k", "v2");
tm.commit();
----

In this example, TX1 and TX2 are executed in parallel.
The initial value of "k" is "v".

* If `forceReturnValue = true`, the `cache.put()` operation fetches the value for "k" from the server in both TX1 and TX2.
The transaction that acquires the lock for "k" first then commits.
The other transaction rolls back during the commit phase because the transaction can detect that "k" has a value other than "v".

* If `forceReturnValue = false`, the `cache.put()` operation does not fetch the value for "k" from the server and returns null.
Both TX1 and TX2 can successfully commit, which results in a conflict.
This occurs because neither transaction can detect that the initial value of "k" changed.

The following transactions include `cache.get()` operations to read the value for "k" before doing the `cache.put()` operations:

.Transaction 1 (TX1)
[source,java]
----
RemoteCache<String, String> cache = ...
TransactionManager tm = ...

tm.begin();
cache.get("k");
cache.put("k", "v1");
tm.commit();
----

.Transaction 2 (TX2)
[source,java]
----
RemoteCache<String, String> cache = ...
TransactionManager tm = ...

tm.begin();
cache.get("k");
cache.put("k", "v2");
tm.commit();
----

In the preceding examples, TX1 and TX2 both read the key so the `forceReturnValue` parameter does not take effect.
One transaction commits, the other rolls back.
However, the `cache.get()` operation requires an additional server request.
If you do not need the return value for the `cache.put()` operation that server request is inefficient.

[[hr_transactions_ex_use_config]]
== Using the Configured Transaction Manager and Transaction Mode

The following example shows how to use the `TransactionManager` and `TransactionMode` that you configure in the `RemoteCacheManager`:

[source,java]
----
//Configure the transaction manager and transaction mode.
org.infinispan.client.hotrod.configuration.ConfigurationBuilder cb = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder();
cb.remoteCache("my-cache")
    .transactionManagerLookup(RemoteTransactionManagerLookup.getInstance())
    .transactionMode(TransactionMode.NON_XA);

RemoteCacheManager rcm = new RemoteCacheManager(cb.build());

//The my-cache instance uses the RemoteCacheManager configuration.
RemoteCache<String, String> cache = rcm.getCache("my-cache");

//Return the transaction manager that the cache uses.
TransactionManager tm = cache.getTransactionManager();

//Perform a simple transaction.
tm.begin();
cache.put("k1", "v1");
System.out.println("K1 value is " + cache.get("k1"));
tm.commit();
----
