The Tablestore SDK for Java runs a local transaction for a specified partition key value so that the writes in the transaction are all committed or all discarded. The isolation level is Read Committed.
Prerequisites
-
Install the Tablestore SDK for Java and initialize the client.
-
Enable local transactions on the table. You can enable the feature when you create a table. To enable local transactions on an existing table or check whether the feature is enabled, submit a ticket.
Description
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 local transaction has the following three stages:
-
startLocalTransactionstarts the transaction and returns the transaction ID. -
Every subsequent read or write attaches the ID with
setTransactionId(txnId). -
commitTransactioncommits the transaction;abortTransactionaborts it.
The supported operations inside a transaction are GetRow, PutRow, UpdateRow, DeleteRow, BatchWriteRow, and GetRange.
public StartLocalTransactionResponse startLocalTransaction(StartLocalTransactionRequest request) throws TableStoreException, ClientException
public CommitTransactionResponse commitTransaction(CommitTransactionRequest request) throws TableStoreException, ClientException
public AbortTransactionResponse abortTransaction(AbortTransactionRequest request) throws TableStoreException, ClientException
public void setTransactionId(String transactionId)
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
Start transaction request
StartLocalTransactionRequest contains the following parameters.
|
Name |
Type |
Description |
|
tableName (required) |
String |
The name of the data table. |
|
primaryKey (required) |
PrimaryKey |
The partition key value that defines the transaction scope. When you start a local transaction, specify only the first primary key column of the table. |
|
rowKeys (optional) |
List<PrimaryKey> |
The primary keys of the rows to lock when the transaction starts. Call |
Requests within a transaction
Data read and write requests used inside a transaction inherit TxnRequest, which contains the following parameter.
|
Name |
Type |
Description |
|
transactionId (required) |
String |
The local transaction ID returned by |
Commit or abort request
CommitTransactionRequest and AbortTransactionRequest contain the following parameter.
|
Name |
Type |
Description |
|
transactionID (required) |
String |
The local transaction ID to commit or abort. Specify the ID when you construct the request. |
Response
Start transaction result
StartLocalTransactionResponse contains the following operation-specific field.
|
Name |
Type |
Description |
|
transactionID |
String |
The ID of the new local transaction. Call |
Limitations
-
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
BatchWriteRowrequest 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);