Transactions with the JavaScript Client

What You Will Learn

How to use the JavaScript Hot Rod client’s transaction manager to group cache operations into atomic units that are either committed or rolled back together. You will also learn how read-then-write transactions track entry versions for conflict detection.

Prerequisites

  • Node.js 22+

  • An Infinispan Server running on localhost:11222

Start an Infinispan Server with Docker or Podman:

docker run -it --rm -p 11222:11222 -e USER=admin -e PASS=password quay.io/infinispan/server:latest
Tip
You can replace docker with podman in the command above if you use Podman.

Step 1: Create a Transactional Cache

Transactional caches require NON_XA mode and PESSIMISTIC locking in their configuration:

await adminClient.admin.getOrCreateCache('txCache',
    '<distributed-cache>' +
    '<encoding><key media-type="text/plain"/><value media-type="text/plain"/></encoding>' +
    '<transaction mode="NON_XA" locking="PESSIMISTIC"/>' +
    '</distributed-cache>');

Step 2: Begin, Put, and Commit

Get the transaction manager from the client, begin a transaction, put values, and commit. Puts are buffered locally until commit, when they are sent to the server atomically via the Hot Rod PREPARE_TX operation:

const tm = client.getTransactionManager();
await tm.begin();

await client.put('key1', 'value1');
await client.put('key2', 'value2');

await tm.commit();

During a transaction, get() returns locally buffered values before falling back to the server:

await tm.begin();
await client.put('key1', 'value1');
const v = await client.get('key1'); // returns 'value1' from local buffer
await tm.commit();

Step 3: Rollback

Roll back a transaction to discard all buffered changes:

await tm.begin();
await client.put('key4', 'should-not-exist');
await tm.rollback();
// key4 does not exist on the server

Step 4: Read-then-Write

When you read a key inside a transaction, the client records its version. On commit, the server verifies that the version has not changed, providing conflict detection:

await tm.begin();
const current = await client.get('counter');       // version recorded
await client.put('counter', String(parseInt(current) + 1));
await tm.commit();                                  // server checks version

Step 5: Remove within a Transaction

Removes are also buffered and applied atomically on commit:

await tm.begin();
await client.remove('temp');
const v = await client.get('temp'); // returns undefined (locally removed)
await tm.commit();
// 'temp' is removed from the server

Step 6: Run the Tutorial

npm run transactions

You should see output like:

=== Commit transaction ===
Put 3 keys (buffered locally).
Get key1 within tx: value1
Transaction committed.
  key1 = value1
  key2 = value2
  key3 = value3

=== Rollback transaction ===
Put key4 (buffered locally).
Transaction rolled back.
  key4 = undefined

=== Read-then-write transaction ===
Read counter within tx: 0
Transaction committed.
  counter = 1

=== Remove within transaction ===
Get temp within tx after remove: undefined
Transaction committed.
  temp = undefined

Cache cleared.

What’s Next