I'm reading about DynamoDB transactions and it says:
You can't target the same item with multiple operations within the same transaction. For example, you can't perform a ConditionCheck and also an Update action on the same item in the same transaction.
However, when you go to the DynamoDB transaction examples, I see:
Update markItemSold = new Update()
.withTableName(PRODUCT_TABLE_NAME)
.withKey(productItemKey)
.withUpdateExpression("SET ProductStatus = :new_status")
.withExpressionAttributeValues(expressionAttributeValues)
.withConditionExpression("ProductStatus = :expected_status")
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
So which is it? Is it that case that "you can't perform a ConditionCheck and also an Update action on the same item in the same transaction" or not?
You're conflating two different aspects of conditional checks.
In TransactWriteItems
, there are 4 API requests that you can make:
Each of these are different entities.
Within each of these API's you can apply a ConditionExpression which is what you highlight in your code. Your code is a single Update
operation which includes a ConditionExpression
, which is different to using a ConditionCheck
.
This is fine:
Update markItemSold = new Update()
.withTableName(PRODUCT_TABLE_NAME)
.withKey(productItemKey)
.withUpdateExpression("SET ProductStatus = :new_status")
.withExpressionAttributeValues(expressionAttributeValues)
.withConditionExpression("ProductStatus = :expected_status")
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
This is not fine, as its two operations on the same item:
Update markItemSold = new Update()
.withTableName(PRODUCT_TABLE_NAME)
.withKey(productItemKey)
.withUpdateExpression("SET ProductStatus = :new_status")
.withExpressionAttributeValues(expressionAttributeValues)
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
ConditionCheck checkProductStatus = new ConditionCheck()
.withTableName(PRODUCT_TABLE_NAME)
.withKey(productItemKey)
.withConditionExpression("ProductStatus = :expected_status")
.withExpressionAttributeValues(expressionAttributeValues);
ConditionCheck
in a transaction:Imagine you are an event promoter and each event has a finite number of tickets which users can purchase. You have two tables, one to hold the available tickets per event, the other to vend tickets to users who purchase them:
CAPACITY_TABLE |PK| Tickets ---|--- Event#123 | 33 Event#203 | 221
PURCHASE_TABLE |PK| SK | TicketsPurchased ---|---|--- User001 | Event#123 | 2 | User302 | Event#123 | 2 | User291 | Event#203 | 1 |
Now User333
comes to purchase two tickets for Event#123
. To do it all in a single atomic request, we can use transactions, one Put
to purchase the tickets, and one ConditionCheck
to make sure we have available capacity:
// Purchase ticket
Put purchase = new Put()
.withTableName(PURCHASE_TABLE)
.withItem(eventTickets)
// Check availability
ConditionCheck checkCapacity = new ConditionCheck()
.withTableName(CAPACITY_TABLE)
.withKey(eventKey)
.withConditionExpression("Tickets > :purchased")
.withExpressionAttributeValues(expressionAttributeValues);