Multi-Region and Geo-Distributed Systems Questions
Running a system across regions and continents: multi-region replication, data residency and sovereignty, geo-routing, cross-region consistency, and conflict resolution during failover. Covers the latency, cost, and consistency tradeoffs of going global and how to keep regional failures isolated. Global distribution strategy at the service layer.
Explain semi-synchronous replication, where the primary waits for at least one replica to acknowledge a commit before responding to the client. Discuss configuration recommendations for cross-region setups: number of acks to wait for, timeout strategies, backoff and failover behaviors, and how to detect and recover when required replicas are unreachable.
Sample Answer
Semi-synchronous replication: the primary blocks commit until at least N replicas acknowledge durable receipt, providing stronger durability than async but less latency than requiring majority consensus. Primary still owns commit decision; if required acks don't arrive in time it can either fallback to async or surface an error.
Recommendations for cross-region SRE setups
- acks to wait for:
- Default: wait for 1 ack in a remote region (best tradeoff: durability for region failure vs single request latency).
- For stronger guarantees (survive primary+one-region loss): wait for 2 acks in different regions or require one local + one remote.
- timeout strategies:
- Base timeouts on measured network RTTs: set commit-wait timeout = max(2×p95 RTT, lower bound e.g. 250–500ms) with an upper cap (e.g. 2s). Use adaptive timeouts that adjust to p50/p95 trends.
- Use per-transaction timeouts and a short overall circuit-breaker window so the primary isn’t indefinitely blocked.
- backoff & failover behavior:
- On timeout, perform exponential backoff for retries (e.g. 100ms→200→400…) and mark the replica unhealthy if consecutive failures exceed threshold.
- Fallback policies: (a) return error to client so app can retry, OR (b) optionally commit asynchronously if configured (must be a conscious durability tradeoff).
- For failover, require health + replication position checks; prefer promotion only when a replica is caught up or use consensus/quorum-based promotion to avoid split-brain.
- detecting and recovering unreachable required replicas:
- Monitor replication lag, ack rates, heartbeat latency, and error counts. Alert when lag > threshold or missed acks exceed X in Y minutes.
- Automatically mark unreachable nodes unhealthy and remove them from the required-ack set; shift required-ack targets to other healthy cross-region replicas.
- Recovery: attempt reconnection with exponential backoff, stream incremental WAL, verify checksums/positions, and perform catch-up before reintroducing as required-ack.
Operational best practices
- Expose metrics (replication_lag, ack_latency, required_ack_failures) and set SLOs for commit latency and durability incidents.
- Document runbooks for degraded modes (async fallback, read-only failover) and test cross-region failovers regularly.
Explain quorum-based replication (majority quorums and read/write quorums). Use examples with 3 and 5 replicas to show how quorum sizes affect availability and safety during a region failure. As an SRE, describe how you'd choose quorum sizes for a global deployment with varying latency between regions.
Sample Answer
Quorum-based replication requires a client read or write to be acknowledged by a subset (a quorum) of replicas so that operations are safe (no lost updates) and available. Common rule: choose read quorum R and write quorum W such that W + R > N and W > N/2 (majority writes) to ensure write/read overlap.
Examples:
- N=3 (three replicas). Majority W = 2. If W=2 and R=2 then W+R=4>3 — safe: every read sees latest committed write. If one region (replica) fails, you still have 2 replicas → both reads and writes succeed. If you instead set R=1, W=2 you gain read availability (reads can hit a single replica) but still keep safety because W+R=3>3 is false (2+1=3, equals N) — borderline: depending on model, equals N ensures overlap; many systems require >N/2 overlap so 2+1=3 is acceptable.
- N=5. Majority W = 3. Typical choice W=3, R=2 gives W+R=5>5 equals N; safer is W=3, R=3 (6>5). If a region with two replicas fails, with W=3 you still can write (3 remaining). If three replicas fail, writes block. Lowering W (e.g., W=2) increases availability but risks conflicting concurrent writes.
SRE decision for global deployment:
- Balance latency vs safety: prefer smaller R for low-latency reads (R=1) with higher W (majority) to preserve safety for writes; use read repair, versioning, or conditional reads to mitigate stale reads.
- For cross-region writes, keep W as majority (W> N/2) to avoid split-brain; route writes to a region with enough replicas or use leader-based replication to centralize writes.
- Use geo-aware placement so majority spans reliable regions, set failure domains (rack/zone/region) constraints, and adjust quorums dynamically (e.g., read from local replica with freshness checks) to meet SLOs while maintaining safety.
- Monitor latency and error budget; during region failure, automate reconfiguration (temporary quorum adjustment with caution) and run chaos tests to validate behavior.
You're rolling out a backward-incompatible schema change (non-nullable column) to a globally replicated relational database. Describe a safe, zero-downtime migration plan across regions that includes phased changes, compatibility layers, rollout steps, verification, and rollback strategy. Mention replication and read-replica considerations.
Sample Answer
Requirements & constraints:
- Roll out non-nullable column without downtime across global regions, with replicated primary + read-replicas, low latency, safe rollback, data correctness.
High-level plan (phased, backwards-compatible first):
- Compatibility layer & schema addition (safe):
- Add new nullable column with default NULL in primary schema. Deploy application logic that writes the new column when possible but tolerates NULL reads (i.e., treat NULL as fallback).
- Add DB-level CHECK/INDEX as needed but avoid NOT NULL.
- Backfill & validation:
- Backfill column values asynchronously on primaries using a controlled job (idempotent, shardable, rate-limited). Track progress in a migration table.
- Validate correctness with sampling, checksums, and compare reads from replicas vs primary.
- Read-replica considerations:
- Ensure replicas are caught up before changing behavior in a region. Use replication lag metrics; block progression if lag > threshold.
- Avoid schema changes that cause replica divergence (use same ALTER in all regions but staged apply after backfill).
- Enforce non-null safely:
- Once backfill complete and app deployed to stop writing NULLs, deploy DB constraint in two steps:
a) Add a NOT NULL constraint using ALTER TABLE ... VALIDATE CONSTRAINT or add a constraint that is checked but not immediate (DB-specific, e.g., PostgreSQL: add NOT NULL with CHECK validated using CONCURRENTLY where possible).
b) Alternatively, create a trigger that rejects NULLs and switch to strict mode in app.
- Rollout steps & verification:
- Canary deploy app change in one region: writes new column and tolerates NULLs; monitor errors, latency, SLOs.
- Start backfill; monitor replication lag, migration-job metrics, data correctness tests.
- Gradually expand regions; wait for replicas to be consistent.
- After all backfills and canaries pass, apply NOT NULL in primary during low-traffic window with ongoing monitoring.
- Verify via health checks, synthetic transactions, and histogram of NULLs = 0.
- Rollback & safety nets:
- If failure detected (data corruption, large lag, app errors), stop backfill jobs, revert app to older version that tolerates NULL, and remove constraint attempt if applied.
- Keep feature flag to disable writes to the new column if needed.
- Use backups and point-in-time restore as last resort; ensure binary logs/audit logging for recovery.
Trade-offs & notes:
- Prefer application-level forward/backward compatibility (feature flags) over risky DB locks.
- Consider online schema change tooling (gh-ost, pt-online-schema-change) for large tables to avoid locks.
- Communication & runbook: document thresholds, escalation, and rollback steps. Continuous monitoring of replication lag and migration progress is critical.
Design an active-active multi-region API platform to achieve 99.99% availability and target p95 latency <50ms for 90% of users globally. Discuss active-active vs active-passive trade-offs, replication models, conflict resolution, DNS/Anycast routing, and validation of SLOs after failover.
Sample Answer
Requirements & constraints:
- Functional: global API platform, read/write API traffic, consistent user experience.
- Non-functional: 99.99% availability (~52 min/year), p95 latency <50ms for 90% users, tolerate region failure, automated failover, data consistency SLAs.
High-level design:
- Deploy identical API clusters in 3+ regions (active-active). Each region has ingress (Anycast+regional load balancer), stateless API pods behind regional autoscaling, regional caches (Redis), and local storage (DB replicas).
- Use global control plane for config/feature flags and health orchestration; data layer uses geo-replication.
Active-active vs active-passive trade-offs:
- Active-active: better latency and availability (traffic served from nearest healthy region), faster capacity scaling, zero-RTT failover. Complexity: distributed data consistency, conflict resolution, operational complexity.
- Active-passive: simpler data model, easier consistency; slower recovery and possible latency spikes for users far from active region; wasted standby capacity.
Replication models & conflict resolution:
- Use a hybrid model:
- Read-mostly data: multi-master eventually-consistent replication (e.g., Dynamo-style, Cassandra, CRDTs) with last-writer-wins where acceptable, or vector clocks for causal info.
- Strong-consistency data: use leader-based consensus (Raft/etcd) confined to a region with cross-region leader election fallback; or use per-tenant sharded leaders to reduce cross-region latency.
- For user-sensitive writes requiring linearizability (billing, identity), prefer single-region leader with synchronous replication to a backup region (semi-sync) and client-aware routing.
- Conflict resolution:
- Design domain-specific merge functions — CRDTs or application-layer compensating transactions.
- Surface conflicts via event streams and automated reconciliation jobs; log & alert conflicts exceeding thresholds.
Routing: DNS, Anycast, and health-aware steering
- Global Anycast IP to announce the same IP from edge PoPs → minimize latency and route to nearest PoP.
- Combine Anycast with regional health checks and Geo-DNS/Traffic Director for topology-aware failover. Use consistent hashing to preserve session affinity where needed.
- Edge checks: active health probes + BGP/Anycast withdraw on catastrophic region failure.
- Client hints & latency probes to steer long-lived connections to optimal region.
Operational considerations & validation:
- Observability: per-region metrics (p95 latency, error rates, RTO), distributed tracing, synthetic monitoring from global vantage points, real-user monitoring (RUM).
- SLO validation after failover:
- Run chaos exercises: simulate region loss using automated failover playbooks; measure p95 and availability during and after event.
- Post-failover runbooks: confirm DNS/Anycast convergence, cache warming, DB quorum behavior, and consumer-visible error budgets.
- Automated canaries: shift small % traffic to target region, verify SLOs, then ramp.
- Alerting: composite alerts for cross-region anomalies and SLO burn rate. Maintain runbooks that map observed symptoms to mitigation steps.
Scalability & resiliency patterns:
- Circuit breakers, retries with jitter, backpressure (429), graceful degradation (serve cached responses, disable non-critical features).
- Capacity planning: reserve headroom per region and use auto-scaling with predictive workloads.
Trade-offs and final notes:
- Active-active maximizes availability and latency goals but requires careful data partitioning and conflict handling. Prefer hybrid consistency: CRDTs/eventual for tolerant domains; leader-based strong consistency for critical domains.
- Validate SLOs continuously via synthetic and chaos tests, and maintain automated rollback/traffic-shift tooling to meet 99.99% target during incidents.
Write a Python script (or clear pseudocode) that streams two CSV snapshots of a key-value store from RegionA and RegionB and emits keys present in RegionA but missing or with an older 'version' in RegionB. Files may be larger than memory; design for streaming. CSV columns: key,value,version_ts (ISO8601). Explain complexity and how you'd run at scale.
Sample Answer
Approach (brief): snapshots may not fit in memory and are unsorted. Split each CSV into N shard files by hashing the key (so identical keys land in same shard). Then process shards one-by-one: load one shard from RegionB into memory as a dict (fits because shard size = total/N), stream corresponding RegionA shard and emit keys missing in B or with older version_ts. Clean up temp files. This is embarrassingly parallel and streaming-friendly.
Python implementation (streaming + sharding):
import csv, os, tempfile, hashlib
from datetime import datetime
from pathlib import Path
def shard_path(base_dir, prefix, idx):
return Path(base_dir) / f"{prefix}_shard_{idx}.csv"
def hash_shard(key, n_shards):
return int(hashlib.md5(key.encode()).hexdigest(), 16) % n_shards
def create_shards(src_path, prefix, n_shards, base_dir):
writers = {}
files = {}
try:
for i in range(n_shards):
p = shard_path(base_dir, prefix, i)
f = open(p, "w", newline='')
files[i] = f
writers[i] = csv.writer(f)
with open(src_path, newline='') as fh:
r = csv.reader(fh)
for row in r:
key = row[0]
i = hash_shard(key, n_shards)
writers[i].writerow(row)
finally:
for f in files.values():
f.close()
def parse_ts(s):
return datetime.fromisoformat(s)
def compare_shards(base_dir, n_shards, out_writer):
for i in range(n_shards):
a_path = shard_path(base_dir, "A", i)
b_path = shard_path(base_dir, "B", i)
# load B shard into memory (key -> version_ts)
b_map = {}
if b_path.exists():
with open(b_path, newline='') as fb:
for key, val, ver in csv.reader(fb):
b_map[key] = parse_ts(ver)
if not a_path.exists():
continue
with open(a_path, newline='') as fa:
for key, val, ver in csv.reader(fa):
a_ts = parse_ts(ver)
b_ts = b_map.get(key)
if b_ts is None or a_ts > b_ts:
out_writer.writerow([key, val, ver, "" if b_ts is None else b_ts.isoformat()])
def cleanup(base_dir):
for p in Path(base_dir).glob("*_shard_*.csv"):
p.unlink()
# Example run
def run(regionA_csv, regionB_csv, n_shards=256, out_csv="diffs.csv"):
base = tempfile.mkdtemp(prefix="shards_")
try:
create_shards(regionA_csv, "A", n_shards, base)
create_shards(regionB_csv, "B", n_shards, base)
with open(out_csv, "w", newline='') as fout:
writer = csv.writer(fout)
writer.writerow(["key","value_A","version_ts_A","version_ts_B"])
compare_shards(base, n_shards, writer)
finally:
cleanup(base)
os.rmdir(base)
Key points and reasoning:
- Hash-sharding ensures all records for a key go to same shard so we can compare locally.
- Memory per shard ≈ total_size / N, tune N so each shard fits in RAM.
- Streaming read/write avoids loading full files.
- Parsing ISO8601 with datetime.fromisoformat; adapt for timezone formats if needed.
Complexity:
- I/O: O(|A| + |B|) bytes written/read twice (shard-write then shard-read).
- CPU: O(|A| + |B|) hashing + parsing.
- Memory: O(max_shard_size) ≈ O((|A|+|B|)/N).
Running at scale / operational notes:
- Increase N or use cloud storage (S3) and run shards in parallel (multiprocessing, AWS Batch, EMR, or Kubernetes jobs).
- Use compressed files and streaming decompression to reduce network I/O.
- For very large/continuous snapshots consider sorting with external sort (GNU sort --parallel) and a single merge pass as alternative.
- Add checksums, monitoring, and retries; emit metrics (counts processed, diffs) and ship diffs to downstream systems or alerts.
Unlock Full Question Bank
Get access to all Multi-Region and Geo-Distributed Systems interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.