Skip to content

Multi-Region Data, Ordering, and Stores

First PublishedByAtif Alam

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.

ConcernRead-heavyWrite-heavy
Replication lagOften tolerableDangerous if stale reads feed decisions
Conflict potentialLowerHigher
DB choiceTypical RDBMS + replicasOften global DB, sharding, or careful funneling
Hotspot riskLowerHigher

Mixed workloads are common: tier write paths—strict ordering for a subset (payments, inventory), last-write-wins for preferences, events for analytics.

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.

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.

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 typeOrdering needStrategy
Financial / ledgerStrict total orderFunnel to write region or single log
State machineCausal / step orderOptimistic locking, versions
Activity eventsApproximate orderPartition by user or shard
Cache invalidationNoneBroadcast or pub/sub
PreferencesLast-write-winsRegional write with async sync

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.

  1. Write commits with sequence N (or version).
  2. Publish invalidation (pub/sub or queue) so regional caches drop affected keys.
  3. 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.

Data typeTTLRationale
CatalogMinutesChanges infrequently
User profile1–2 minutesModerate churn
Order / transaction10–30 secondsOrdering and staleness risk
SessionMatch sessionSecurity boundary
Aggregates30–60 secondsCostly to recompute

You do not have to pick the most expensive global database first.

PhaseTypical storesNotes
EarlySingle primary RDBMS, async replica for DRSimple; RPO from lag
GrowthRegional read replicas, cache per regionRead latency improves
AdvancedGlobal DB (CockroachDB, Spanner), DynamoDB Global Tables for selected domains, Kafka/MSK for ordered pipelinesMatch 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.

Requirements in this series: high RPS, latency budget, read-heavy with some ordered writes, multi-region.

  • 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.
  • TrueTime-style timestamps and strong global consistency at scale.
  • Tradeoff: cloud affinity, cost, operational model on GCP.
  • 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.).
RoleExample direction
Ordered, strongly consistent coreCockroachDB or Spanner (if on GCP)
Sessions, flags, high-churn KVDynamoDB or Redis
Audit / downstreamKafka or managed equivalent

One coherent pattern:

  • Global routingregional API + regional cacheCockroachDB 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).

LayerRead pathWrite 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.

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.

What you do depends on how data was replicated while the region was away or partitioned:

PatternConsolidation
Single primary + async replicaCatch up the replica; no merge if only the primary wrote
Multi-master / async with overlapReplay lag, detect conflicts, apply LWW, app merge, or manual repair
Kafka / logConsumer offsets; replay from last committed; watch duplicates with idempotency
Consensus DB (CockroachDB / Spanner)Cluster rebalances; verify under-replicated ranges resolved; still validate application invariants
CacheInvalidate 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.

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.