Read-Your-Writes on Global Tables: Survive Cross-Region Replication Lag

Read-Your-Writes on Global Tables: Survive Cross-Region Replication Lag

DynamoDB Global Tables give you multi-region active-active with asynchronous replication. After a write in ap-south-1, a read in eu-west-1 can miss it for hundreds of milliseconds (or more under load). Users who submit a form and immediately GET their profile see ghosts unless you design for read-your-writes (RYW).

⚡ TL;DR: Stick the session to the region that took the write; or return a version/ConsistentRead token the client echoes; or read from the writer region until a version vector says replicas caught up. Multi-region checkout patterns: Production-Grade Multi-Region E-Commerce Checkout on AWS. Idempotent writes still matter: Lambda Powertools Idempotency.

Session stickiness (simplest RYW)

Route a user sticky to one region for the session. Writes and reads share a replica; cross-region is for failover and users elsewhere.

# edge: choose region from cookie
def region_for(request) -> str:
    r = request.cookies.get("ryw_region")
    if r in {"ap-south-1", "eu-west-1", "us-east-1"}:
        return r
    return request.geo_nearest_region()

Failover must clear or rewrite the cookie when the primary is unhealthy.

Version tokens for cross-region reads

On write, return version (monotonic per item) or DynamoDB item attribute incremented atomically. Client sends If-Match-Version / X-Min-Version on subsequent reads; if the local replica is behind, proxy read to the writer region or wait briefly.

# write path
resp = table.update_item(
    Key={"pk": pk},
    UpdateExpression="SET profile = :p ADD version :one",
    ExpressionAttributeValues={":p": profile, ":one": 1},
    ReturnValues="UPDATED_NEW",
)
return {"version": int(resp["Attributes"]["version"])}

# read path
def read_profile(pk: str, min_version: int | None, local, writer):
    item = local.get_item(Key={"pk": pk}).get("Item")
    if not min_version:
        return item
    if item and int(item.get("version", 0)) >= min_version:
        return item
    # ✅ Fall back to writer region for RYW
    return writer.get_item(Key={"pk": pk}).get("Item")
# ❌ Always ConsistentRead in every region — GT does not make remote ConsistentRead see latest global write
table.get_item(Key=..., ConsistentRead=True)

When lag becomes an incident

Instrument replication lag proxies: time from write timestamp attribute to local visibility (canaries that write in A and poll B). Alert when p99 lag exceeds UX budget. During regional congestion, prefer stickiness over cross-region read storms.

Conflict resolution

Two regions writing the same key need last-writer-wins awareness or application merge. RYW does not replace conflict design—document which fields are region-owned.

Closing checklist

✅ Dos
– ✅ Sticky sessions for the common RYW case
– ✅ Return and honor version tokens for cross-region UX
– ✅ Canary measure A→B visibility lag
– ✅ Fail sticky cookie over on regional failover
– ✅ Keep idempotency keys on multi-region writes

❌ Don’ts
– ❌ Don’t assume ConsistentRead is globally consistent on Global Tables
– ❌ Don’t fan-out every read to all regions
– ❌ Don’t ignore dual-write conflicts on hot keys
– ❌ Don’t hide lag behind unbounded client sleeps
– ❌ Don’t store RYW tokens that never expire in localStorage without care

Related reading

Last updated on September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

1 Comment

Leave a Reply