All Products
Search
Document Center

Tablestore:Conditional updates

Last Updated:Nov 30, 2023

You can use the conditional update feature to update data in a data table only if the specified conditions are met. If the conditions are not met, the update fails.

Scenarios

The conditional update feature is applicable to scenarios in which a large number of clients concurrently update a data table. This feature ensures data correctness and consistency.

In these scenarios, old_value may be updated by other clients. You can use the conditional update feature to update the current value to new_value only if the current value is equal to old_value.

Important

In scenarios such as page view (PV) counting or gaming, conditional updates may fail. In these cases, data update retries are required.

A conditional update is implemented in the following way:

  1. Obtain the current value.

  2. Check whether the current value meets the conditions.

    • If the conditions are met, update the current value to the new value.

    • If the conditions are not met, the update fails.

Feature description

When you call the PutRow, UpdateRow, DeleteRow, or BatchWriteRow operation to update data in a data table, you can specify row existence conditions and column-based conditions to perform conditional updates. The data in the data table is updated only if the conditions are met.

You can use the conditional update feature to implement optimistic locking. If you want to update a row, you can obtain the value of a column first. For example, Column A has a value of 1. Then, you can set a condition that the value of Column A is 1 and use a conditional update to update the value to 2. If the update fails, the row has been updated by other clients.

You can specify row existence conditions and column-based conditions for a conditional update.

  • Column-based condition

    Column-based conditions include SingleColumnValueConditions and CompositeColumnValueConditions, which are used to perform condition-based judgment based on the values of one or more columns. Column-based conditions are similar to the conditions that are used by Tablestore filters.

    Column-based conditions support the following relational and logical operators: =, !=, >, >=, <, <= NOT, AND, and OR. You can specify up to 10 column-based conditions for a conditional update.

  • Row existence condition

    When you update a data table, Tablestore first checks whether row existence conditions are met. If the row existence conditions are not met, the update fails and an error is reported.

    The following row existence conditions are supported: IGNORE, EXPECT_EXIST, and EXPECT_NOT_EXIST.

    The following table describes the update rules based on row existence conditions for different operations.

    Note

    A BatchWriteRow operation consists of multiple suboperations, including PutRow, UpdateRow, and DeleteRow. If you update data in a data table by calling the BatchWriteRow operation, see the update rules for the specific operations.

    Operation

    IGNORE

    EXPECT_EXIST

    EXPECT_NOT_EXIST

    PutRow on an existing row

    Succeed

    Succeed

    Fail

    PutRow on a non-existent row

    Succeed

    Fail

    Succeed

    UpdateRow on an existing row

    Succeed

    Succeed

    Fail

    UpdateRow on a non-existent row

    Succeed

    Fail

    Succeed

    DeleteRow on an existing row

    Succeed

    Succeed

    Fail

    DeleteRow on a non-existent row

    Succeed

    Fail

    Succeed

Use the feature

You can use the conditional update feature by using the Tablestore CLI or Tablestore SDKs.

Use the Tablestore CLI

You can use the conditional update feature when you write data or update data in the Tablestore CLI.

  • Specify a row existence condition when you run the put command to insert a row of data. For more information, see the "Insert a row of data" section of the Operations on data topic.

    The following sample command inserts a row into a data table. The value of the first primary key column in the row is "86". The value of the second primary key column in the row is 6771. The row contains the following two attribute columns: name and country. The name and country columns are of the STRING type. Data is inserted regardless of whether the row exists. If the row exists, the inserted data overwrites the existing data.

    put --pk '["86", 6771]' --attr '[{"c":"name", "v":"redchen"}, {"c":"country", "v":"china"}]' --condition ignore
  • Specify a row existence condition when you run the update command to update a row of data. For more information, see the "Update a row of data" section of the Operations on data topic.

    The following sample command updates a row of data in which the value of the first primary key column is "86" and the value of the second primary key column is 6771. Data is updated regardless of whether the row exists. If the row does not exist, the row of data is inserted.

    update --pk '["86", 6771]' --attr '[{"c":"name", "v":"redchen"}, {"c":"country", "v":"china"}]' --condition ignore

Use Tablestore SDKs

You can use Tablestore SDK for Java, Tablestore SDK for Go, Tablestore SDK for Python, Tablestore SDK for Node.js, Tablestore SDK for . NET, and Tablestore SDK for PHP to use the conditional update feature. In the following examples, Tablestore SDK for Java is used.

Update data based on a column-based condition

  • Construct a SinglleColumnValueCondition

    In the following example, the value of the Col0 column is updated if the value meets the condition of Col0==0.

    private static void updateRowWithSingleColumnValueCondition(SyncClient client, String pkValue){
        // Construct the primary key. 
        PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString(pkValue));
        PrimaryKey primaryKey = primaryKeyBuilder.build();
        
        // Read a row of data. 
        SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("<TABLE_NAME>", primaryKey);
        criteria.setMaxVersions(1);
        GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
        Row row = getRowResponse.getRow();
        long col0Value = row.getLatestColumn("Col0").getValue().asLong();
    
        // Specify the name of the data table. 
        RowUpdateChange rowUpdateChange = new RowUpdateChange("<TABLE_NAME>", primaryKey);
    
        // Set the condition: Col0==0. 
        SingleColumnValueCondition singleColumnValueCondition = new SingleColumnValueCondition("Col0",
                SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(0));
        // If the Col0 column does not exist, the conditional check fails. 
        singleColumnValueCondition.setPassIfMissing(false);
        // Specify that only the latest version is used for comparison. 
        singleColumnValueCondition.setLatestVersionsOnly(true);
        Condition condition = new Condition();
        condition.setColumnCondition(singleColumnValueCondition);
        rowUpdateChange.setCondition(condition);
        // If the value of the Col0 column meets the column-based condition, the value of the Col0 column is increased by 1. 
        rowUpdateChange.put(new Column("Col0", ColumnValue.fromLong(col0Value+1)));
        try {
            client.updateRow(new UpdateRowRequest(rowUpdateChange));
            System.out.println("Data updated. ");
        } catch (TableStoreException ex) {
            System.out.println("Failed to update data. " + ex.toString());
        }
    }                 
  • Construct a CompositeColumnValueCondition

    In the following example, the value of the Col0 column is updated if the value meets one of the following conditions: Col0==0 AND Col1>100 and Col2<=10.

    private static void updateRowWithCompositeColumnValueCondition(SyncClient client, String pkValue){
        // Construct the primary key. 
        PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        primaryKeyBuilder.addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString(pkValue));
        PrimaryKey primaryKey = primaryKeyBuilder.build();
            
    		// Read a row of data. 
        SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("<TABLE_NAME>", primaryKey);
        criteria.setMaxVersions(1);
        GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
        Row row = getRowResponse.getRow();
        long col0Value = row.getLatestColumn("Col0").getValue().asLong();
    
        // Specify the name of the data table. 
        RowUpdateChange rowUpdateChange = new RowUpdateChange("<TABLE_NAME>", primaryKey);
    
        // Set the composite1 condition to (Col0 == 0) AND (Col1 > 100). 
        CompositeColumnValueCondition composite1 = new CompositeColumnValueCondition(CompositeColumnValueCondition.LogicOperator.AND);
        SingleColumnValueCondition single1 = new SingleColumnValueCondition("Col0",
                SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(0));
        SingleColumnValueCondition single2 = new SingleColumnValueCondition("Col1",
                SingleColumnValueCondition.CompareOperator.GREATER_THAN, ColumnValue.fromLong(100));
        composite1.addCondition(single1);
        composite1.addCondition(single2);
    
        // Set the composite2 condition to ((Col0 == 0) AND (Col1 > 100)) OR (Col2 <= 10). 
        CompositeColumnValueCondition composite2 = new CompositeColumnValueCondition(CompositeColumnValueCondition.LogicOperator.OR);
        SingleColumnValueCondition single3 = new SingleColumnValueCondition("Col2",
                SingleColumnValueCondition.CompareOperator.LESS_EQUAL, ColumnValue.fromLong(10));
        composite2.addCondition(composite1);
        composite2.addCondition(single3);
    
        Condition condition = new Condition();
        condition.setColumnCondition(composite2);
        rowUpdateChange.setCondition(condition);
    		//  If the value of the Col0 column meets one of the column-based conditions, the value of the Col0 column is increased by 1. 
        rowUpdateChange.put(new Column("Col0", ColumnValue.fromLong(col0Value+1)));
        try {
            client.updateRow(new UpdateRowRequest(rowUpdateChange));
            System.out.println("Data updated. ");
        } catch (TableStoreException ex) {
            System.out.println("Failed to update data. " + ex.toString());
        }
    }

Update data based on optimistic locking

The following sample code provides an example on how to increase the value of a column based on optimistic locking.

 private static void updateRowWithCondition(SyncClient client, String pkValue) {
     // Construct the primary key. 
     PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
     primaryKeyBuilder.addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString(pkValue));
     PrimaryKey primaryKey = primaryKeyBuilder.build();

     // Read a row of data. 
     SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("<TABLE_NAME>", primaryKey);
     criteria.setMaxVersions(1);
     GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
     Row row = getRowResponse.getRow();
     long col0Value = row.getLatestColumn("Col0").getValue().asLong();

     // Configure conditional update to increase the value of the Col0 column by 1. 
     RowUpdateChange rowUpdateChange = new RowUpdateChange("<TABLE_NAME>", primaryKey);
     Condition condition = new Condition(RowExistenceExpectation.EXPECT_EXIST);
     ColumnCondition columnCondition = new SingleColumnValueCondition("Col0", SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(col0Value));
     condition.setColumnCondition(columnCondition);
     rowUpdateChange.setCondition(condition);
     rowUpdateChange.put(new Column("Col0", ColumnValue.fromLong(col0Value + 1)));

     try {
         client.updateRow(new UpdateRowRequest(rowUpdateChange));
     } catch (TableStoreException ex) {
         System.out.println(ex.toString());
     }
 }                  

Billing

Tablestore supports the following billing modes: reserved mode and pay-as-you-go mode. The billing of capacity units (CUs) varies based on billing modes.

The consumed CUs are calculated based on the specific operation that is performed if data is successfully written or updated by a conditional update. If the conditional update fails, one write CU and one read CU are consumed. You can use metered read and write CUs or reserved read and write CUs based on your instance types.

Note

For more information about instance types and CUs, see Instance and Read and write throughput.