All Products
Search
Document Center

Tablestore:Local transactions

Last Updated:Sep 20, 2026

After you enable local transactions for a data table, create a local transaction based on a specified partition key value and perform read and write operations on data in the transaction. Local transactions support atomic read and write operations on one or more rows.

Scenarios

The local transaction feature lets you perform atomic operations to read and write one or more rows. The following section describes sample scenarios:

Simple scenarios: Read and write data

Use the following two methods to perform read, modify, and write (RMW) operations. Each method has specific limits.

  • Conditional update: processes only one request that involves a single row at a time. You cannot use this method to process requests that involve multiple data rows or requests that involve multiple write operations. For more information, see Conditional updates.

  • Atomic counter: processes only one request that involves a single row at a time, and supports only the increment of column values. For more information, see Use the atomic counter feature.

To resolve the preceding issues, you can create a local transaction to perform RMW operations on data within the range specified by a partition key value.

  1. Call the StartLocalTransaction operation to create a local transaction based on the specified partition key value and obtain the local transaction ID.

  2. Call the GetRow or GetRange operation to read data. The request must contain the local transaction ID.

  3. Modify data on the client.

  4. Call the PutRow, UpdateRow, DeleteRow, or BatchWriteRow operation to write back the modified data. The request must contain the local transaction ID.

  5. Call the CommitTransaction operation to commit the local transaction.

Complex scenario: mailbox scenario

Create a local transaction to perform atomic operations on the emails of a specific user.

To use the local transaction feature correctly, create two index tables on a data table. The following table lists the primary key columns for these tables. In the table, the Type column distinguishes the data table from the index tables. Each index row uses the IndexField column to store a field with a specific meaning, whereas the data table does not contain an IndexField column.

Table

Primary key column

UserID

Type

IndexField

MailID

Data table

User ID

"Main"

N/A

Email ID

Folder index table

User ID

"Folder"

$Folder

Email ID

SendTime index table

User ID

"SendTime"

$SendTime

Email ID

Specifically, you can use the local transaction feature to perform the following operations on emails.

Scenario 1: List the last 100 emails sent by a user

  1. Use the user ID to create a local transaction and obtain the local transaction ID.

  2. Call the GetRange operation to query 100 emails from the SendTime index table. The request must contain the local transaction ID.

  3. Call the BatchGetRow operation to query the detailed information of the 100 emails from the data table. The request must contain the local transaction ID.

  4. Call the CommitTransaction operation to commit the local transaction, or call the AbortTransaction operation to abort the local transaction.

Scenario 2: Transfer all emails in a folder to another folder

  1. Use the user ID to create a local transaction and obtain the local transaction ID.

  2. Call the GetRange operation to query emails from the Folder index table. The request must contain the local transaction ID.

  3. Call the BatchWriteRow operation to perform write operations on the Folder index table. The request must contain the local transaction ID.

    A write operation is performed on two rows each time when an email is transferred. Specifically, a row that indicates the original folder is removed from the Folder index table, and a row that indicates the new folder is added to the Folder index table.

  4. Call the CommitTransaction operation to commit the local transaction.

Scenario 3: Count the numbers of read emails and unread emails in a folder

  1. Use the user ID to create a local transaction and obtain the local transaction ID.

  2. Call the GetRange operation to query emails from the Folder index table. The request must contain the local transaction ID.

  3. Call the BatchGetRow operation to query the read status of each email from the data table.

  4. Call the CommitTransaction operation to commit the local transaction, or call the AbortTransaction operation to abort the local transaction.

This solution is not optimal. In this scenario, add more index tables to accelerate queries. The local transaction feature ensures status consistency between the data table and index tables, which simplifies development. For example, this solution for counting emails requires reading many emails and causes high overhead. To reduce overhead and accelerate queries, use a new index table to store the numbers of read and unread emails.

Before you begin

You must enable local transactions when you create a data table. Use the Tablestore console, Tablestore SDK for Java V5.11.0 or later, or the latest version of Tablestore SDK for Go. For more information, see Create a data table.

Important

If you need local transactions for an existing data table but did not enable the feature when the table was created, submit a ticket. You may also join DingTalk group 36165029092 (Tablestore Technical Discussion Group-3) for assistance

Usage notes

  • You cannot use the auto-increment primary key column feature and the local transaction feature at the same time.

  • Pessimistic locking is used to control concurrent operations in a local transaction.

  • The validity period of a local transaction can be up to 60 seconds.

    If a local transaction is not committed or aborted within 60 seconds, the Tablestore server determines that the local transaction times out and aborts the transaction.

  • A transaction may be created on the Tablestore server even if a timeout error is returned. In this case, you can resend a transaction creation request after the created transaction times out.

  • If a local transaction is not committed, it may become invalid. In this case, retry the operations in this transaction.

  • If no write operation is performed on the data in a local transaction, the commit and abort operations have the same effect.

  • Tablestore imposes the following limits on read and write operations on the data in a local transaction:

    • The local transaction ID cannot be used to access data beyond the range specified based on the partition key value that is used to create the transaction.

    • The partition key values of all write requests in the same transaction must be the same as the partition key value used to create the transaction. This limit does not apply to read requests.

    • A local transaction can be used by only one request at a time. When the local transaction is in use, other operations that use the same local transaction ID fail.

    • The maximum interval for two consecutive read or write operations on the data in a local transaction is 60 seconds.

      If no read or write operation is performed on the data in a local transaction for more than 60 seconds, the Tablestore server determines that the transaction times out and aborts the transaction.

    • Up to 4 MB of data can be written to each transaction. The volume of data written to each transaction is calculated in the same way as a regular write request.

    • If you do not specify a version number for a cell, the Tablestore server automatically assigns a version number to the cell in the usual way when the cell is written to the transaction rather than the way when the transaction is committed.

    • If a BatchWriteRow request includes a local transaction ID, all rows in the request can be written only to the table that matches the local transaction ID.

    • When you use a local transaction, a write lock is added to the data of the partition key value based on which the local transaction is created. Only write requests that contain the local transaction ID and are initiated to write data in the local transaction can be successful. Other non-transactional requests or write requests that contain the IDs of other local transactions and are initiated to write data in the local transaction will fail. Data in the local transaction is unlocked if the transaction is committed or aborted, or if the transaction times out.

    • A local transaction remains valid even if a read or write request with the local transaction ID is rejected. Specify a retry rule to resend the request, or abort the transaction.

Procedure

Important

Manage local transactions only by using Tablestore SDKs.

To use local transactions, you first create a local transaction for a partition key value, then read and write data within the transaction, and finally commit or abort the transaction as needed. The following examples use Tablestore SDK for Java.

Use the local transaction feature to write a data row

The following example creates a local transaction for a specified partition key value of a table and writes a row within the local 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);

Use the local transaction feature to read a data row

The following example creates a local transaction for a specified partition key value of a table and reads a row within the local transaction.

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);

SDK integration

Use local transactions with the following SDKs.

Billing

  • Each StartLocalTransaction, CommitTransaction, and AbortTransaction operation consumes one write capacity unit (CU).

  • Read and write operations are billed similarly to standard read and write requests. For more information about billing, see Billing overview.

Error codes

Error code

Description

OTSRowOperationConflict

The partition key value is already occupied by another local transaction.

OTSSessionNotExist

The transaction corresponding to the specified transaction ID does not exist, or the transaction is invalid or timed out.

OTSSessionBusy

The last request on the transaction is incomplete.

OTSOutOfTransactionDataSizeLimit

The amount of data in the transaction exceeds the upper limit.

OTSDataOutOfRange

The data operation is beyond the range specified by the partition key value used to create the transaction.