All Products
Search
Document Center

Tablestore:Use local transactions

Last Updated:Jun 25, 2026

Local transactions group multiple reads and writes on a single partition key value into one atomic unit at the Read Committed (RC) isolation level. All operations either commit together or roll back together, which suits atomic single-row and multi-row read and write scenarios.

Prerequisites

  • Tablestore SDK for Java installed and the client initialized.

  • Local transactions enabled when creating a data table.

    Note

    To enable local transactions on an existing table or to check whether the feature is enabled, submit a ticket.

How it works

// Methods on SyncClient
public StartLocalTransactionResponse startLocalTransaction(StartLocalTransactionRequest request) throws TableStoreException, ClientException
public CommitTransactionResponse commitTransaction(CommitTransactionRequest request) throws TableStoreException, ClientException
public AbortTransactionResponse abortTransaction(AbortTransactionRequest request) throws TableStoreException, ClientException

// Carry the transaction ID on every transactional request (subclasses of TxnRequest)
public void setTransactionId(String transactionId)

A local transaction is scoped to a single partition key value. Reads and writes inside the transaction are linked by a transaction ID and take effect atomically on commit, with Read Committed isolation. A transaction uses three core methods:

  • startLocalTransaction starts the transaction and returns the transaction ID.

  • Every subsequent read or write attaches the ID with setTransactionId(txnId).

  • commitTransaction commits the transaction; abortTransaction aborts it.

The supported operations inside a transaction are GetRow, PutRow, UpdateRow, DeleteRow, BatchWriteRow, and GetRange.

The following example starts a local transaction on partition key value pkvalue, writes a row with primary key (pkvalue, 10001), and commits the transaction.

String tableName = "local_tx_demo";

// 1. Start a local transaction for the specified partition key value and obtain the transaction ID.
PrimaryKeyBuilder pkBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pkBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
PrimaryKey partitionKey = pkBuilder.build();

StartLocalTransactionRequest startRequest =
        new StartLocalTransactionRequest(tableName, partitionKey);
String txnId = client.startLocalTransaction(startRequest).getTransactionID();

// 2. Write a row within the transaction. You must specify the full primary key and include the transaction ID.
PrimaryKeyBuilder rowKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
rowKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
rowKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(10001));
PrimaryKey rowKey = rowKeyBuilder.build();

RowPutChange rowPutChange = new RowPutChange(tableName, rowKey);
rowPutChange.addColumn(new Column("col1", ColumnValue.fromString("colvalue")));
rowPutChange.addColumn(new Column("col2", ColumnValue.fromLong(10)));

PutRowRequest putRequest = new PutRowRequest(rowPutChange);
putRequest.setTransactionId(txnId);
client.putRow(putRequest);

// 3. Commit the transaction to make all writes take effect. To discard changes, call abortTransaction() instead.
CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId);
client.commitTransaction(commitRequest);

Parameters

Name

Type

Description

tableName (required)

String

The name of the data table.

primaryKey (required)

PrimaryKey

The primary key of the data table.

  • When starting a local transaction, specify only the partition key value.

  • When reading or writing inside the transaction, specify the full primary key (all primary key columns).

transactionId (required)

String

The local transaction ID returned by startLocalTransaction, used to uniquely identify a local transaction.

Every read or write request inside the transaction must carry this ID by calling setTransactionId(txnId).

Usage notes

  • Local transactions are not compatible with auto-increment primary key columns.

  • Local transactions use pessimistic locking for concurrency control. During a transaction, a write lock is held on the partition key value, so only write requests that carry the transaction ID succeed.

  • A transaction has a maximum lifetime of 60 seconds. If two consecutive operations are more than 60 seconds apart, the transaction times out and the server discards it.

  • Only one request can use a transaction ID at a time. Concurrent operations that share the same ID all fail.

  • Every write request inside a transaction must use the same partition key value that started the transaction. Read requests have no such restriction.

  • A single transaction can write up to 4 MB of data.

  • If a write inside a transaction does not specify a column version (timestamp), the server generates the timestamp at write time, not at commit time. The rules match those for a normal write.

  • When a BatchWriteRow request carries a transaction ID, all rows must target the table on which the transaction was started.

  • If the transaction contains no writes, commit and abort have the same effect.

  • A failed read or write request that carries a transaction ID does not end the transaction. You can apply a retry policy or abort the transaction explicitly.

Examples

Read a row inside a transaction

Create a read-only transaction for a specified partition key value and read a row.

String tableName = "local_tx_demo";

// 1. Start a local transaction for the specified partition key value.
PrimaryKeyBuilder pkBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pkBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
PrimaryKey partitionKey = pkBuilder.build();

StartLocalTransactionRequest startRequest =
        new StartLocalTransactionRequest(tableName, partitionKey);
String txnId = client.startLocalTransaction(startRequest).getTransactionID();

// 2. Read a row within the transaction. You must specify the full primary key and include the transaction ID.
PrimaryKeyBuilder rowKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
rowKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
rowKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(10001));
PrimaryKey rowKey = rowKeyBuilder.build();

SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(tableName, rowKey);
criteria.setMaxVersions(1);

GetRowRequest getRequest = new GetRowRequest(criteria);
getRequest.setTransactionId(txnId);
GetRowResponse getResponse = client.getRow(getRequest);

// 3. Commit or abort the transaction. For a read-only transaction, both have the same effect and release the transaction.
CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId);
client.commitTransaction(commitRequest);

Row row = getResponse.getRow();
System.out.println(row);

Batch write multiple rows inside a transaction

Attach the transaction ID to a batch write by calling BatchWriteRowRequest.setTransactionId(txnId). The partition key value of every row must match the value used when the transaction was started, and the commit applies all rows atomically.

String tableName = "local_tx_demo";

// 1. Start a local transaction. The partition key value of every row in the batch must match this value.
PrimaryKeyBuilder pkBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pkBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
PrimaryKey partitionKey = pkBuilder.build();

StartLocalTransactionRequest startRequest =
        new StartLocalTransactionRequest(tableName, partitionKey);
String txnId = client.startLocalTransaction(startRequest).getTransactionID();

// 2. Build the batch write request and include the transaction ID.
BatchWriteRowRequest batchRequest = new BatchWriteRowRequest();
batchRequest.setTransactionId(txnId);

// Add multiple rows (pk1 of every row must equal the transaction's partition key value "pkvalue").
for (long pk2 = 20001; pk2 <= 20003; pk2++) {
    PrimaryKeyBuilder rowKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    rowKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
    rowKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(pk2));
    RowPutChange rowPutChange = new RowPutChange(tableName, rowKeyBuilder.build());
    rowPutChange.addColumn(new Column("col1", ColumnValue.fromString("batch_" + pk2)));
    batchRequest.addRowChange(rowPutChange);
}

BatchWriteRowResponse batchResponse = client.batchWriteRow(batchRequest);
System.out.println("Batch all succeeded: " + batchResponse.isAllSucceed());

// 3. Commit the transaction so that all batch writes take effect atomically.
CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId);
client.commitTransaction(commitRequest);

References