What you'll learn
- Race conditions occur when parallel updates overwrite each other.
- The ADD action atomically increments numeric values in DynamoDB.
- Atomic increments are not idempotent: retrying an uncertain write can count twice. Use deduplication or a transaction when exact counts matter.
Section 01
The Race Condition Problem
Imagine thousands of players battling at once. A traditional read-modify-write pattern is dangerous:
- Read current stats (Wins: 10)
- Application adds 1 (Wins: 11)
- Write back (Wins: 11)
Ready · 16 steps · Orbi
This animation plays when you scroll to it. Press Play to start it now.
All steps as text
- Trainer Ash sends GetItem to the Pokemon table with Name = "Mew". Read the counter first.
- DynamoDB hashes "Mew" to 416a585c, which routes the request to partition P0.
- P0 returns Mew (BattlesWon 0).
- Trainer Gary sends GetItem to the Pokemon table with Name = "Mew". Reads before A has written.
- DynamoDB hashes "Mew" to 416a585c, which routes the request to partition P0.
- P0 returns Mew (BattlesWon 0).
- Trainer Ash sends UpdateItem to the Pokemon table with Name = "Mew". Writes back 0 + 1: SET BattlesWon = :wins
- DynamoDB hashes "Mew" to 416a585c, which routes the request to partition P0.
- P0 updates Mew (BattlesWon = 1).
- This call consumed 1 WCU.
- DynamoDB responds with success: Ash's write succeeded.
- Trainer Gary sends UpdateItem to the Pokemon table with Name = "Mew". Also writes back 0 + 1: SET BattlesWon = :wins
- DynamoDB hashes "Mew" to 416a585c, which routes the request to partition P0.
- Trainer Gary's write to Mew lands on P0 and silently replaces the other change.
- This call consumed 1 WCU.
- DynamoDB responds with success: two wins recorded, but BattlesWon shows 1.
Section 02
Atomic Counters to the Rescue
DynamoDB supports atomic updates. You tell it "Add 1 to this attribute" rather than "Set this attribute to 11". Each update is atomic, so concurrent increments do not overwrite one another. A timeout can still leave the outcome uncertain; blindly retrying can increment again.
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, UpdateCommand } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({ region: "ap-south-1" });
const docClient = DynamoDBDocumentClient.from(client);
await docClient.send(new UpdateCommand({
TableName: "Pokemon",
Key: { Name: "Mew" },
UpdateExpression: "ADD BattlesWon :inc",
ExpressionAttributeValues: {
":inc": 1
}
}));Practice
Your turn
Mew won another battle. Use
UpdateCommand with the ADD action to increase BattlesWon by 1. It's atomic, so it beats reading the value and writing it back.Use
ADD in UpdateExpression to increment BattlesWon by 1.Tab inserts two spaces. Press Escape, then Tab, to leave the editor. Control or Command plus Enter runs the code.
Ctrl/⌘ Enter to run · simulated, no AWS callsSimulated — no AWS calls
Check
Check your understanding
Quick Quiz · one question, as many tries as you need.
Why should you use atomic counters instead of read-modify-write for game stats?
Choose one answer.