Using DynamoDB Transactions to Keep Inventory and Order Data Consistent
Learn how the TransactWriteItems API groups multiple updates into an all‑or‑nothing operation, preventing partial inventory decrements when a condition fails.
14 Jul 2025, 20:24 UTC

When a simple update can leave data out of sync
Imagine an e‑commerce flow where a purchase must both reduce the available stock of a product and create an order record. If you issue two separate PutItem or UpdateItem calls, a network glitch or a throttling error after the first call could leave the stock decremented while the order never appears—or vice‑versa. That inconsistency is hard to detect and can corrupt inventory reports.
The thesis is straightforward: DynamoDB Transactions let you bundle those related writes into a single request that either all succeed or all roll back, giving you atomicity without building a custom lock service.
How TransactWriteItems works
The TransactWriteItems API accepts up to 25 action objects (Put, Update, Delete, or ConditionCheck). DynamoDB evaluates all condition checks first; if any fail, the entire transaction is aborted and no changes are persisted. If all checks pass, the writes are applied together. The service automatically handles the rollback, so you never see a half‑applied state.
Key points to remember:
- Each action still consumes read/write capacity (or on‑demand pricing) as if it were issued individually, plus a small overhead for transaction coordination.
- Latency typically rises by ~10‑20 ms compared to separate calls.
- Transactions are limited to 25 actions and cannot include operations that return more than 1 MB of data (e.g., large
Queryresults).
Worked example: Python with boto3
The following snippet shows a purchase that decrements stock and inserts an order. A condition on the stock attribute ensures we never sell more than we have.
import boto3
from botocore.exceptions import ClientError
def purchase_item(table_name, product_id, quantity):
dynamodb = boto3.client('dynamodb', region_name='us-east-1')
try:
response = dynamodb.transact_write_items(
TransactItems=[
{
'Update': {
'TableName': table_name,
'Key': {'product_id': {'S': product_id}},
'UpdateExpression': 'SET stock = stock - :qty',
'ConditionExpression': 'stock >= :qty',
'ExpressionAttributeValues': {':qty': {'N': str(quantity)}}
}
},
{
'Put': {
'TableName': table_name,
'Item': {
'order_id': {'S': 'order-#{}}'.format(product_id)},
'product_id': {'S': product_id},
'quantity': {'N': str(quantity)},
'status': {'S': 'PENDING'}
}
}
}
]
)
# If we reach here, both actions succeeded
print('Transaction succeeded:', response)
return response
except ClientError as e:
# A condition check failure or any other error triggers a full rollback
if e.response['Error']['Code'] == 'TransactionCanceledException':
print('Transaction canceled – stock check likely failed')
else:
print('Unexpected error:', e)
raise
# Example usage:
# purchase_item('InventoryTable', 'SKU-123', 2)
Where to run: Execute this code in any environment with AWS credentials that have dynamodb:TransactWriteItems permission on the target table. No special IAM role beyond standard DynamoDB access is required.
Expected checks: If the stock attribute is less than the requested quantity, the condition check fails, DynamoDB rolls back both the stock update and the order insertion, and the function catches a TransactionCanceledException. You can verify the outcome by reading the item after the call—both the stock value and the order record should be unchanged from their pre‑call state.
Trade‑offs and practical tips
While transactions give you safety, they come with cost and complexity considerations:
- Capacity overhead: Each action’s capacity consumption is reported separately in the response; the sum is usually slightly higher than issuing the calls individually due to transaction coordination.
- Retry logic: Throttling or validation errors trigger a
TransactionCanceledException. Implement exponential backoff with jitter to avoid overwhelming the service. - Monitoring: Enable CloudWatch metrics for
TransactionConflictsandTransactionWriteCount. A rising conflict rate may indicate hot keys that need redesign (e.g., splitting counters). - Fallback patterns: Some business rules expect partial success (e.g., record a failed order while still decrementing inventory for a separate reservation). In those cases, you may need to compensate outside the transaction or avoid using it.
Verification steps you can perform in a test account:
- Create an on‑demand DynamoDB table with a
product_idhash key and attributesstock(Number) and any order‑related fields you need. - Insert an item with
stock = 10. - Run the snippet above with a quantity greater than the current stock (e.g., 15). Observe the caught
TransactionCanceledException. - Read the item again; confirm that
stockremains 10 and no order record exists. - Repeat with a valid quantity (e.g., 2) and verify both the stock decrement and the new order item appear.
- Check the
ConsumedCapacityin the response to see the extra units used by the transaction.
Actionable closing
If your application needs to keep multiple DynamoDB items in sync—stock levels, ledgers, or any pair of related records—start by wrapping the updates in a TransactWriteItems call. Keep the transaction small, monitor conflict metrics, and build retry handling. The safety net of automatic rollback often outweighs the modest latency and capacity cost, especially when data correctness is critical.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.