Multi-Region Data, Ordering, and Stores
Read-heavy vs write-heavy workloads
Section titled “Read-heavy vs write-heavy workloads”Read-heavy workloads are more forgiving for multi-region: you can often use one write primary, read replicas per region, and aggressive caching. Replication lag matters less for non-critical reads.
Write-heavy workloads raise conflict risk, hot keys, schema migration coordination, and may force multi-master or strict funneling of writes—see CAP theorem and Consistency models.
| Concern | Read-heavy | Write-heavy |
|---|---|---|
| Replication lag | Often tolerable | Dangerous if stale reads feed decisions |
| Conflict potential | Lower | Higher |
| DB choice | Typical RDBMS + replicas | Often global DB, sharding, or careful funneling |
| Hotspot risk | Lower | Higher |
Mixed workloads are common: tier write paths—strict ordering for a subset (payments, inventory), last-write-wins for preferences, events for analytics.
Ordered writes across regions
Section titled “Ordered writes across regions”Ordering and multi-region writes are in tension: there is no global clock that orders every request across regions without a single sequencer or a shared log.
Option 1: funnel ordered writes (common)
Section titled “Option 1: funnel ordered writes (common)”All writes that need a total order go to a designated write region (or primary), regardless of where the user sits:
- User → regional API → forward to write region → DB serializes → replicate with order preserved.
- Pros: Simple reasoning; no cross-region conflicts for those entities.
- Cons: Higher write latency for distant users; write region outage pauses ordered writes until failover completes.
Option 2: distributed log as the backbone
Section titled “Option 2: distributed log as the backbone”Use a durable log (for example Kafka-style topics) with partitioning that defines order (for example one partition for strict global order, or partition by entity id for per-entity order). Consumers apply writes in log order.
- Pros: Clear ordering semantics; replay from offset on recovery.
- Cons: Throughput and latency must fit your SLO; ops for the cluster.
Option 3: hybrid by entity type (pragmatic)
Section titled “Option 3: hybrid by entity type (pragmatic)”| Write type | Ordering need | Strategy |
|---|---|---|
| Financial / ledger | Strict total order | Funnel to write region or single log |
| State machine | Causal / step order | Optimistic locking, versions |
| Activity events | Approximate order | Partition by user or shard |
| Cache invalidation | None | Broadcast or pub/sub |
| Preferences | Last-write-wins | Regional write with async sync |
Regional caching
Section titled “Regional caching”For read-heavy traffic at scale, regional cache (for example Redis-compatible services) is standard:
- Request → L1 cache (hit → return) → on miss → read replica → populate cache → return.
- Prefer invalidation over blind update on write for correctness when ordering matters: commit then publish invalidation so the next read loads fresh data from the replica.
Redis vs Memcached: managed Redis-style systems often support replication, richer data structures, and pub/sub for invalidation—useful across regions. Memcached is simpler but has a weaker cross-region story.
Cache invalidation with ordered writes
Section titled “Cache invalidation with ordered writes”- Write commits with sequence N (or version).
- Publish invalidation (pub/sub or queue) so regional caches drop affected keys.
- Next read misses, loads from replica, repopulates cache.
Include version or etag in keys where staleness after a fast region update could otherwise serve an older cached value from a slow region.
Example TTL hints (tune to your SLOs)
Section titled “Example TTL hints (tune to your SLOs)”| Data type | TTL | Rationale |
|---|---|---|
| Catalog | Minutes | Changes infrequently |
| User profile | 1–2 minutes | Moderate churn |
| Order / transaction | 10–30 seconds | Ordering and staleness risk |
| Session | Match session | Security boundary |
| Aggregates | 30–60 seconds | Costly to recompute |
Phased store choice
Section titled “Phased store choice”You do not have to pick the most expensive global database first.
| Phase | Typical stores | Notes |
|---|---|---|
| Early | Single primary RDBMS, async replica for DR | Simple; RPO from lag |
| Growth | Regional read replicas, cache per region | Read latency improves |
| Advanced | Global DB (CockroachDB, Spanner), DynamoDB Global Tables for selected domains, Kafka/MSK for ordered pipelines | Match product to consistency needs |
Postgres with replication (including logical replication or managed equivalents) fits many early multi-region read patterns. CockroachDB / Spanner enter when you need serializable global behavior without owning all sequencing in app code. DynamoDB Global Tables excel at scale and ops simplicity but use last-write-wins—poor fit for strict ordering as a primary store for those paths.
Greenfield database evaluation (summary)
Section titled “Greenfield database evaluation (summary)”Requirements in this series: high RPS, latency budget, read-heavy with some ordered writes, multi-region.
CockroachDB (strong general fit)
Section titled “CockroachDB (strong general fit)”- Postgres-compatible wire protocol; familiar SQL and many ORMs.
- Distributed SQL with consensus; regional placement options for rows.
- Serializable isolation helps ordered semantics without ad-hoc locking everywhere.
- Follower reads for low-latency reads with bounded staleness.
- Tradeoff: cost and Raft latency vs single-node Postgres; ops simpler than self-managing multi-region Postgres for many teams.
Google Spanner (GCP)
Section titled “Google Spanner (GCP)”- TrueTime-style timestamps and strong global consistency at scale.
- Tradeoff: cloud affinity, cost, operational model on GCP.
DynamoDB Global Tables (AWS)
Section titled “DynamoDB Global Tables (AWS)”- Serverless scale; multi-region replication with roughly second-scale replication in many setups.
- Tradeoff: LWW conflicts; not a drop-in for arbitrary serializable cross-region transactions—often paired with purpose-specific access patterns.
Postgres with HA (Patroni, managed RDS-style)
Section titled “Postgres with HA (Patroni, managed RDS-style)”- Full control and cost advantages; mature ecosystem.
- Tradeoff: you own failover, lag monitoring, connection storm risks at high RPS—use poolers (PgBouncer, RDS Proxy, etc.).
Practical split
Section titled “Practical split”| Role | Example direction |
|---|---|
| Ordered, strongly consistent core | CockroachDB or Spanner (if on GCP) |
| Sessions, flags, high-churn KV | DynamoDB or Redis |
| Audit / downstream | Kafka or managed equivalent |
Reference stack sketch
Section titled “Reference stack sketch”One coherent pattern:
- Global routing → regional API + regional cache → CockroachDB with follower reads on read-heavy paths → Kafka for audit and async consumers.
Write path: API → CRDB consensus write → optional async event to Kafka → cache invalidation.
Read path: API → cache → on miss follower read (with explicit staleness bound where acceptable).
Latency budget (illustrative)
Section titled “Latency budget (illustrative)”| Layer | Read path | Write path |
|---|---|---|
| Global routing | ~few ms | ~few ms |
| Regional API | ~10 ms | ~10 ms |
| Cache hit | ~2–5 ms | — |
| DB read (follower) | ~10–20 ms | — |
| Consensus write | — | ~tens of ms (depends on topology) |
| Kafka emit (async) | — | small enqueue cost |
Actual numbers depend on region pairs and instance sizes—measure.
Schema migrations in multi-region
Section titled “Schema migrations in multi-region”At high write volume, one-shot DDL in one region before another is dangerous: regions can disagree on schema during rollout. Prefer expand–contract: phases where both old and new schemas are valid, backward-compatible writes, then cut over readers, then remove old paths.
Reconciliation after a region returns
Section titled “Reconciliation after a region returns”What you do depends on how data was replicated while the region was away or partitioned:
| Pattern | Consolidation |
|---|---|
| Single primary + async replica | Catch up the replica; no merge if only the primary wrote |
| Multi-master / async with overlap | Replay lag, detect conflicts, apply LWW, app merge, or manual repair |
| Kafka / log | Consumer offsets; replay from last committed; watch duplicates with idempotency |
| Consensus DB (CockroachDB / Spanner) | Cluster rebalances; verify under-replicated ranges resolved; still validate application invariants |
| Cache | Invalidate or rebuild; avoid trusting stale entries after failback |
Idempotency and ordering choices determine how painful merge is: if every effect is idempotent and keyed, replays are safer.
Idempotency and retries
Section titled “Idempotency and retries”Retries across regions without idempotency keys can duplicate charges, events, or rows. Treat idempotency keys on mutating endpoints as mandatory where correctness matters; combine with optimistic locking (version columns) when two writers might touch the same row.