Skip to content

Create and use transactions

This page describes how to create and use transactions, which were introduced in Aerospike Database 8.0.0. A transaction is an encapsulation of several commands, isolated from commands outside the transaction, and executed atomically.

To ensure strict serializability, transactions in Aerospike require CP consistency mode, known in Aerospike as the strong-consistency (SC) namespace configuration. A transaction can guarantee one of two outcomes: either all commands succeed together, or one or more commands fail, in which case you can request to roll back to the state of the records prior to the attempted transaction. No commands outside the transaction can see the state changes being created inside the transaction.

Transaction best practices

  • Remember that transactions and single-record commands can be mixed in the same SC namespace.
  • Make sure to call abort when your app gives up on a transaction. Not calling abort invokes unnecessary monitor activity, and leaves records “locked” for much longer than necessary.
  • Use batch-writes to write a group of independent records in a transaction. A batch is especially efficient inside a transaction.
  • Don’t mix expiration and non-durable deletes with transactions. This is a general recommendation for strong consistency namespaces.
  • Pay attention to tombstone accumulation and tomb-raider configs.
  • Do not truncate in a namespace with active transactions. If you must use transactions, see Pause and drain transactions before truncating for steps to first disable and drain existing transactions.
  • Do not mix transactions with active-active XDR, unless you’re certain that you are not writing to the same records on both sides. Use stretch clusters (multi-site clustering) instead of XDR if you intend to use transactions this way.

Create a transaction

The following steps describe how to create a transaction, bind it to commands, and commit. A transaction object is attached to each command’s policy so the server can logically group related requests.

  1. Create a transaction.

    import com.aerospike.client.Txn;
    Txn txn = new Txn();
    System.out.println("Begin txn: " + txn.getId());
  2. Issue commands within a try block and attach the transaction to each command’s policy.

    try {
    WritePolicy wp = client.copyWritePolicyDefault();
    wp.txn = txn;
    Key key1 = new Key(namespace, set, 1);
    client.put(wp, key1, new Bin("a", "val1"));
    }
  3. Commit the transaction.

    System.out.println("Commit txn: " + txn.getId());
    client.commit(txn);

How to handle transaction errors

When an exception is thrown during a transaction, abort the transaction to roll back all changes and release locked records.

catch (AerospikeException ae) {
client.abort(txn);
if (ae.getResultCode() == ResultCode.MRT_BLOCKED) {
// Transaction was blocked by another transaction — retry
}
if (ae.getResultCode() == ResultCode.MRT_EXPIRED) {
// Transaction expired before commit or abort — retry
}
throw ae;
}
catch (Throwable t) {
client.abort(txn);
throw t;
}

Status codes

The following status codes can be returned during a transaction:

  • MRT_BLOCKED: The transaction was blocked by another transaction accessing the same record.
  • MRT_EXPIRED: The transaction deadline was reached without a commit or abort.
if (ae.getResultCode() == ResultCode.MRT_BLOCKED) {
// Transaction was blocked by another transaction — retry
}
if (ae.getResultCode() == ResultCode.MRT_EXPIRED) {
// Transaction expired before commit or abort — retry
}

Failed commits

A commit may fail if the read verify step fails. Aerospike attempts to do the read, even if it is dirty during the command execution step. Aerospike later sends an exception with the MRT_VERSION_MISMATCH result code during the commit if the read is determined to be invalid due to version mismatch. This indicates that another command outside the transaction changed the targeted record. The correct way to handle this situation is to retry the entire transaction.

There is also a chance that the transaction succeeds but the commit fails. An example of this would be if there is a problem updating the transaction monitor on the server. In these situations, Aerospike returns a commit exception, and if there is uncertainty about whether the transaction was committed fully, it sets the inDoubt flag to true.

To safely handle this situation, catch the exception from the commit and implement separate handling logic as follows.

try {
client.commit(txn);
}
catch (AerospikeException.Commit ce) {
if (ce.getInDoubt()) {
try {
client.commit(txn);
}
catch (AerospikeException.Commit ce2) {
if (ce2.getInDoubt()) {
// Recommit still in doubt — log records for later cleanup
}
}
}
else {
if (ce.getResultCode() == ResultCode.MRT_VERSION_MISMATCH) {
// Read was invalidated by an outside write — retry transaction
}
else {
// Other commit failure — retry transaction
}
}
}
catch (Throwable t) {
// Handle non-Aerospike exception
}

Examples

Setting a transaction timeout

You can set a timeout (in seconds) that dictates the total allowed time to complete all of the commands in the transaction and commit. The timeout clock starts when the first write request for the transaction is submitted; read requests do not start the timeout clock.

Txn txn = new Txn();
txn.setTimeout(20);
try {
WritePolicy wp = client.copyWritePolicyDefault();
wp.txn = txn;
// Timeout clock starts after this operation
Key key1 = new Key(namespace, set, 1);
client.put(wp, key1, new Bin("a", "val1"));
Key key2 = new Key(namespace, set, 2);
client.put(wp, key2, new Bin("b", "val2"));
}
catch (Throwable t) {
client.abort(txn);
throw t;
}
client.commit(txn);

Batched writes

For batched writes inside a transaction, attach the transaction to the batch policy.

Txn txn = new Txn();
BatchPolicy bp = client.copyBatchPolicyDefault();
bp.txn = txn;
BatchWritePolicy bwp = client.copyBatchWritePolicyDefault();
Key[] keys = new Key[] {
new Key(namespace, set, 1),
new Key(namespace, set, 2),
new Key(namespace, set, 3)
};
try {
client.operate(bp, bwp, keys,
Operation.put(new Bin("color", "blue"))
);
}
catch (AerospikeException ae) {
client.abort(txn);
throw ae;
}
client.commit(txn);

Put, get, and delete in the same transaction

The following example shows multiple commands tied to the same transaction.

Txn txn = new Txn();
try {
WritePolicy wp = client.copyWritePolicyDefault();
wp.txn = txn;
Key key1 = new Key(namespace, set, 1);
client.put(wp, key1, new Bin("a", "val1"));
Policy p = client.copyReadPolicyDefault();
p.txn = txn;
Key key2 = new Key(namespace, set, 3);
Record rec = client.get(p, key2);
WritePolicy dp = client.copyWritePolicyDefault();
dp.txn = txn;
dp.durableDelete = true;
client.delete(dp, key2);
}
catch (Throwable t) {
client.abort(txn);
throw t;
}
client.commit(txn);
Feedback

Was this page helpful?

What type of feedback are you giving?

What would you like us to know?

+Capture screenshot

Can we reach out to you?