Consistency Models
In a single-server database (like SQLite), consistency is easy. If you write X=5, the next read sees X=5.
In a distributed system like DynamoDB, data is replicated across three Availability Zones (AZs) for durability. This introduces a delay—Replication Lag.
You must choose between Speed (Eventual Consistency) and Correctness (Strong Consistency).
1. The CAP Theorem in Practice
DynamoDB is an AP system by default (Available & Partition Tolerant), but can act as CP (Consistent & Partition Tolerant) on a per-request basis.
1. Eventual Consistency (The Default)
- Behavior: When you write, DynamoDB acknowledges success once the Leader node writes the data. Replication to other AZs happens asynchronously (~10-20ms later).
- Risk: If you read from a replica immediately after writing, you might see old data.
- Benefit: Fastest latency, lowest cost (0.5 RCU per 4KB), highest availability.
- Use Case: Social media feeds, comments, likes.
2. Strong Consistency
- Behavior: DynamoDB ensures the read reflects all successful writes. It usually consults the Leader node.
- Risk: Higher latency. If the Leader is unavailable (rare), the read fails (Availability drops).
- Cost: 2x more expensive (1.0 RCU per 4KB).
- Use Case: Financial transactions, inventory counts, “Check if username taken”.
2. Interactive: Replication Simulator
Visualize how data propagates and why “Eventual Consistency” can return stale data.
Replication Lag Visualizer
Leader (AZ1)
v1
Replica (AZ2)
v1
Replica (AZ3)
v1
System ready. All nodes at v1.
3. Code Implementation: Consistent Reads
To enforce Strong Consistency, simply set the ConsistentRead flag to true.
Java Implementation
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import java.util.Map;
public class GetItemStronglyConsistent {
public static void main(String[] args) {
DynamoDbClient ddb = DynamoDbClient.create();
GetItemRequest request = GetItemRequest.builder()
.tableName("Orders")
.key(Map.of("OrderId", AttributeValue.builder().s("101").build()))
.consistentRead(true) // <--- The Toggle
.build();
GetItemResponse response = ddb.getItem(request);
System.out.println("Item: " + response.item());
}
}
Go Implementation
package main
import (
"context"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)
func main() {
cfg, _ := config.LoadDefaultConfig(context.TODO())
svc := dynamodb.NewFromConfig(cfg)
input := &dynamodb.GetItemInput{
TableName: aws.String("Orders"),
Key: map[string]types.AttributeValue{
"OrderId": &types.AttributeValueMemberS{Value: "101"},
},
ConsistentRead: aws.Bool(true), // <--- The Toggle
}
result, err := svc.GetItem(context.TODO(), input)
if err != nil {
log.Fatalf("Error: %v", err)
}
fmt.Println("Item:", result.Item)
}