await ddb.send(new UpdateItemCommand({
TableName: "Accounts",
Key: { pk: { S: "ACCOUNT#42" } },
UpdateExpression: "SET balance = :b, version = :next",
ConditionExpression: "version = :expected",
ExpressionAttributeValues: {
":b": { N: "90" }, ":next": { N: "8" }, ":expected": { N: "7" },
},
}));
This is optimistic locking with a conditional write. The update only runs if the stored version is still 7, the value the app read earlier. If another writer changed the item in the meantime, DynamoDB rejects the write with ConditionalCheckFailedException. The app then reads the item again and retries. Without this check, it would silently overwrite the other writer's change.
Reads are eventually consistent by default. This means a read may briefly return old data. You can ask for a strongly consistent read with ConsistentRead: true, which always returns the latest data. This works on the table or an LSI (local secondary index), and costs double the read cost. GSIs (global secondary indexes) are always eventually consistent.
On-demand capacity bills per request and adjusts to traffic automatically. It suits unpredictable or spiky load, and new apps. Provisioned capacity, usually with auto scaling, is cheaper for steady, predictable traffic.
- A ConditionExpression on the version field implements optimistic locking.
- If the condition fails, DynamoDB raises ConditionalCheckFailedException, and the app re-reads and retries.
- Reads are eventually consistent by default. Set ConsistentRead to get a strongly consistent read.
- Strongly consistent reads cost more and are not available on GSIs.
- Use on-demand capacity for unpredictable load and provisioned capacity for steady load.