After you enable the local transaction feature for a data table, you can create a local transaction based on the specified partition key value and perform read and write operations on the data in the local transaction. You can use the local transaction feature to perform atomic operations to read and write one or more rows.
You can use a local transaction to specify that the operations on data that shares the same partition key either all succeed or all fail. The isolation level of the local transaction is Read Committed.
Prerequisites
An OTSClient instance is initialized. For more information, see Initialize an OTSClient instance.
A data table is created and data is written to the data table.
Procedure
Call the StartLocalTransaction operation to create a local transaction based on the specified partition key value and obtain the local transaction ID.
Read and write data in the local transaction.
You can call the GetRow, PutRow, DeleteRow, UpdateRow, BatchWriteRow, and GetRange operations to perform operations on data in the local transaction.
Call the CommitTransaction operation to commit the local transaction, or call the AbortTransaction operation to abort the local transaction.
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. You can specify a retry rule to resend the request, or you can abort the transaction.
Parameters
Parameter | Required | Description |
TableName | Yes | The name of the data table. |
PrimaryKey | Yes | The primary key of the data table.
|
TransactionId | Yes | The local transaction ID that uniquely identifies a local transaction. You must specify a local transaction ID when you read and write data in a local transaction. |
Examples
Use the local transaction feature to write a row of data
The following sample code provides an example on how to create a local transaction based on the specified partition key value in a table and write a row of data in the local transaction. If a row of data is written in the local transaction, commit the transaction. Otherwise, discard the transaction.
func transactionPutRow(client *tablestore.TableStoreClient, tableName string) {
// Specify a partition key value for the local transaction. The partition key is the first primary key column.
transPk := new(tablestore.PrimaryKey)
transPk.AddPrimaryKeyColumn("pk1", "pk1value")
trans := &tablestore.StartLocalTransactionRequest{
TableName: tableName,
PrimaryKey: transPk,
}
response, err := client.StartLocalTransaction(trans)
if err != nil {
fmt.Println("failed to create transaction", err)
return
}
// Obtain the local transaction ID.
transId := response.TransactionId
putPk := new(tablestore.PrimaryKey)
putPk.AddPrimaryKeyColumn("pk1", "pk1value")
putPk.AddPrimaryKeyColumn("pk2", int64(4))
putRowChange := &tablestore.PutRowChange{
TableName: tableName,
PrimaryKey: putPk,
}
putRowChange.AddColumn("col1", "col1data1")
putRowChange.AddColumn("col2", int64(3))
putRowChange.AddColumn("col3", []byte("test"))
putRowChange.SetCondition(tablestore.RowExistenceExpectation_IGNORE)
// Specify the local transaction ID, which can be obtained by using StartLocalTransactionResponse.TransactionId.
putRowChange.TransactionId = transId
putRowRequest := &tablestore.PutRowRequest{
PutRowChange: putRowChange,
}
_, err = client.PutRow(putRowRequest)
if err != nil {
// If a row of data fails to be written in the local transaction, discard the local transaction. In this case, all data modifications in the local transaction do not apply to the data in the data table.
fmt.Println("putrow failed with error:", err)
request := &tablestore.AbortTransactionRequest{
TransactionId: transId,
}
abortResponse, err := client.AbortTransaction(request)
if err != nil {
fmt.Println("abort transaction failed with error:", err)
} else {
fmt.Println("abort transaction finished. RequestId is", abortResponse.RequestId)
}
} else {
// If a row of data is written in the local transaction, commit the local transaction. In this case, all data modifications in the local transaction take effect. You can discard a local transaction to invalidate all data modifications in the local transaction.
fmt.Println("putrow finished")
request := &tablestore.CommitTransactionRequest{
TransactionId: transId,
}
commitResponse, err := client.CommitTransaction(request)
if err != nil {
fmt.Println("commit transaction failed with error:", err)
} else {
fmt.Println("commit transaction finished. RequestId is", commitResponse.RequestId)
}
}
}
Use the local transaction feature to read a row of data
The following sample code provides an example on how to create a local transaction based on the specified partition key value in a table and read a row of data in the local transaction:
func transactionGetRow(client *tablestore.TableStoreClient, tableName string) {
// Specify a partition key value for the local transaction. The partition key is the first primary key column.
transPk := new(tablestore.PrimaryKey)
transPk.AddPrimaryKeyColumn("pk1", "pk1value")
trans := &tablestore.StartLocalTransactionRequest{
TableName: tableName,
PrimaryKey: transPk,
}
response, err := client.StartLocalTransaction(trans)
if err != nil {
fmt.Println("failed to create transaction", err)
return
}
// Obtain the local transaction ID.
transId := response.TransactionId
// Read data.
getRowPk := new(tablestore.PrimaryKey)
getRowPk.AddPrimaryKeyColumn("pk1", "pk1value")
getRowPk.AddPrimaryKeyColumn("pk2", int64(18))
criteria := &tablestore.SingleRowQueryCriteria{
PrimaryKey: getRowPk,
TableName: tableName,
// Read the latest version of data.
MaxVersion: 1,
// Specify the local transaction ID.
TransactionId: transId,
}
getRowRequest := &tablestore.GetRowRequest{
SingleRowQueryCriteria: criteria,
}
getResp, err := client.GetRow(getRowRequest)
if err != nil {
fmt.Println("getrow failed with error:", err)
} else {
fmt.Println("get row col0 result is ", getResp.Columns[0].ColumnName, getResp.Columns[0].Value)
}
// Commit or discard the local transaction. Committing and discarding a local transaction have the same effect on a read operation.
// Commit the local transaction to allow all data modifications in the local transaction to take effect.
request := &tablestore.CommitTransactionRequest{
TransactionId: transId,
}
commitResponse, err := client.CommitTransaction(request)
if err != nil {
fmt.Println("commit transaction failed with error:", err)
} else {
fmt.Println("commit transaction finished. RequestId is", commitResponse.RequestId)
}
// Discard the local transaction. In this case, all data modifications in the local transaction do not apply to the data in the data table.
//request := &tablestore.AbortTransactionRequest{
// TransactionId: transId,
//}
//abortResponse, err := client.AbortTransaction(request)
//if err != nil {
// fmt.Println("abort transaction failed with error:", err)
//} else {
// fmt.Println("abort transaction finished. RequestId is", abortResponse.RequestId)
//}
}
References
If you want to write multiple rows of data at the same time or read data whose primary key values are within a specific range, create a local transaction. Then, refer to the sample code provided in the Write data or Read data topic to initiate a request that includes the local transaction ID.