Generated from: “Show how the service is laid out across regions and availability zones, with how many copies of each part run.”
View the Markdown
```meta
title: Orders service — regions, zones, and replicas
subtitle: Where each part of the Orders service runs, and how many copies of it exist.
tag: DRAFT
```
The Orders service runs in two regions. `us-east-1` takes all writes and most reads; `eu-west-1` serves European reads and takes over writes only on a declared failover. Each region spreads its parts across three availability zones. The loss of one zone removes at most a third of the capacity and never the only copy of any part.
```callout
tone: note
title: Assumptions in this document
body: "The request did not name the service, the regions, or the parts. This document assumes the Orders service with six parts: gateway, orders-api, orders-worker, Postgres, Redis, Kafka. It assumes the AWS regions us-east-1 and eu-west-1 with three zones each, and an active-passive write model. Change the counts in the table below and carry the change into the zone diagrams."
```
## Two regions, one write primary
Route 53 answers with the nearest healthy region. The Postgres primary lives in `us-east-1`; `eu-west-1` holds an asynchronous replica that lags by under two seconds in normal operation. Kafka topics mirror in the same direction, so `eu-west-1` can serve reads and replay events but never accepts an order write while `us-east-1` is healthy.
```block
preset: infra
title: Region layout
groups:
- { id: global, col: 1, row: 1, cols: 1, rows: 1, label: Global }
- { id: use1, col: 2, row: 1, cols: 1, rows: 4, label: "us-east-1 · primary" }
- { id: euw1, col: 3, row: 1, cols: 1, rows: 4, label: "eu-west-1 · secondary" }
nodes:
- { id: dns, col: 1, row: 1, kind: dns, name: Route 53, tech: latency routing }
- { id: alb-use, col: 2, row: 1, kind: lb, name: Load balancer, tech: ALB }
- { id: api-use, col: 2, row: 2, kind: service, name: orders-api, tech: Go, replicas: 12 }
- { id: pg-use, col: 2, row: 3, kind: store, name: Postgres primary, tech: Postgres 16, replicas: 3 }
- { id: kafka-use, col: 2, row: 4, kind: queue, name: Kafka, tech: "3 brokers", replicas: 3 }
- { id: alb-euw, col: 3, row: 1, kind: lb, name: Load balancer, tech: ALB }
- { id: api-euw, col: 3, row: 2, kind: service, name: orders-api, tech: Go, replicas: 6 }
- { id: pg-euw, col: 3, row: 3, kind: store, name: Postgres replica, tech: Postgres 16, replicas: 2 }
- { id: kafka-euw, col: 3, row: 4, kind: queue, name: Kafka, tech: "3 brokers", replicas: 3 }
edges:
- dns -> alb-use: US and default traffic
- dns -> alb-euw: EU traffic
- alb-use -> api-use
- alb-euw -> api-euw
- api-use -> pg-use: reads and writes
- api-euw -> pg-euw: reads
- api-euw --> pg-use: writes, cross-region
- pg-use --> pg-euw: async replication
- kafka-use --> kafka-euw: MirrorMaker 2
```
## us-east-1, zone by zone
Every stateless part runs in all three zones with the same count, so the load balancer can drop a zone without a config change. The stateful parts differ by role: the Postgres primary is in zone `a`, the synchronous standby in `b`, and the asynchronous standby in `c`. Redis and Kafka each keep one node per zone; Kafka topics use replication factor 3 with `min.insync.replicas=2`, so a single zone outage never blocks a producer.
```cluster
title: us-east-1 compute
clusters:
- { id: use1a, label: us-east-1a, kind: zone }
- { id: use1b, label: us-east-1b, kind: zone }
- { id: use1c, label: us-east-1c, kind: zone }
services:
- { id: gw-a, cluster: use1a, label: gateway, kind: gateway, tech: Envoy, replicas: 2 }
- { id: api-a, cluster: use1a, label: orders-api, kind: service, tech: Go, replicas: 4 }
- { id: wrk-a, cluster: use1a, label: orders-worker, kind: service, tech: Go, replicas: 2 }
- { id: gw-b, cluster: use1b, label: gateway, kind: gateway, tech: Envoy, replicas: 2 }
- { id: api-b, cluster: use1b, label: orders-api, kind: service, tech: Go, replicas: 4 }
- { id: wrk-b, cluster: use1b, label: orders-worker, kind: service, tech: Go, replicas: 2 }
- { id: gw-c, cluster: use1c, label: gateway, kind: gateway, tech: Envoy, replicas: 2 }
- { id: api-c, cluster: use1c, label: orders-api, kind: service, tech: Go, replicas: 4 }
- { id: wrk-c, cluster: use1c, label: orders-worker, kind: service, tech: Go, replicas: 2 }
edges:
- gw-a -> api-a
- gw-b -> api-b
- gw-c -> api-c
- api-a --> wrk-a: order events via Kafka
- api-b --> wrk-b: order events via Kafka
- api-c --> wrk-c: order events via Kafka
```
The data tier is where zones stop being interchangeable. Every `orders-api` copy writes to the primary in zone `a`, whichever zone it runs in. Redis is a cache with no persistence, so a lost Redis node costs a cache warm-up and nothing else.
```cluster
title: us-east-1 data
clusters:
- { id: use1a, label: us-east-1a, kind: zone }
- { id: use1b, label: us-east-1b, kind: zone }
- { id: use1c, label: us-east-1c, kind: zone }
services:
- { id: redis-a, cluster: use1a, label: redis, kind: cache, tech: Redis 7, replicas: 1 }
- { id: kafka-a, cluster: use1a, label: kafka broker 1, kind: queue, tech: Kafka 3.7, replicas: 1 }
- { id: pg-a, cluster: use1a, label: postgres primary, kind: store, tech: Postgres 16, replicas: 1 }
- { id: redis-b, cluster: use1b, label: redis, kind: cache, tech: Redis 7, replicas: 1 }
- { id: kafka-b, cluster: use1b, label: kafka broker 2, kind: queue, tech: Kafka 3.7, replicas: 1 }
- { id: pg-b, cluster: use1b, label: postgres sync standby, kind: store, tech: Postgres 16, replicas: 1 }
- { id: redis-c, cluster: use1c, label: redis, kind: cache, tech: Redis 7, replicas: 1 }
- { id: kafka-c, cluster: use1c, label: kafka broker 3, kind: queue, tech: Kafka 3.7, replicas: 1 }
- { id: pg-c, cluster: use1c, label: postgres async standby, kind: store, tech: Postgres 16, replicas: 1 }
edges:
- pg-a -> pg-b: sync
- pg-a --> pg-c: async
- kafka-a -> kafka-b: partition replicas
- kafka-b -> kafka-c: partition replicas
- kafka-c -> kafka-a: partition replicas
```
## eu-west-1, zone by zone
The secondary region runs half the API count of the primary because it serves reads only. It keeps two Postgres replicas rather than three. Zone `a` receives the cross-region stream and zone `b` cascades from it, so one replica survives if either zone fails. Workers run one per zone; they process mirrored events for read models and stay idle for write-side jobs until a failover promotes the region.
```cluster
title: eu-west-1 compute
clusters:
- { id: euw1a, label: eu-west-1a, kind: zone }
- { id: euw1b, label: eu-west-1b, kind: zone }
- { id: euw1c, label: eu-west-1c, kind: zone }
services:
- { id: egw-a, cluster: euw1a, label: gateway, kind: gateway, tech: Envoy, replicas: 2 }
- { id: eapi-a, cluster: euw1a, label: orders-api, kind: service, tech: Go, replicas: 2 }
- { id: ewrk-a, cluster: euw1a, label: orders-worker, kind: service, tech: Go, replicas: 1 }
- { id: egw-b, cluster: euw1b, label: gateway, kind: gateway, tech: Envoy, replicas: 2 }
- { id: eapi-b, cluster: euw1b, label: orders-api, kind: service, tech: Go, replicas: 2 }
- { id: ewrk-b, cluster: euw1b, label: orders-worker, kind: service, tech: Go, replicas: 1 }
- { id: egw-c, cluster: euw1c, label: gateway, kind: gateway, tech: Envoy, replicas: 2 }
- { id: eapi-c, cluster: euw1c, label: orders-api, kind: service, tech: Go, replicas: 2 }
- { id: ewrk-c, cluster: euw1c, label: orders-worker, kind: service, tech: Go, replicas: 1 }
edges:
- egw-a -> eapi-a
- egw-b -> eapi-b
- egw-c -> eapi-c
- eapi-a --> ewrk-a: mirrored events via Kafka
- eapi-b --> ewrk-b: mirrored events via Kafka
- eapi-c --> ewrk-c: mirrored events via Kafka
```
Zone `c` has no Postgres. Its `orders-api` copies read from zone `a`, which adds under one millisecond of cross-zone latency and keeps the replica count at two.
```cluster
title: eu-west-1 data
clusters:
- { id: euw1a, label: eu-west-1a, kind: zone }
- { id: euw1b, label: eu-west-1b, kind: zone }
- { id: euw1c, label: eu-west-1c, kind: zone }
services:
- { id: eredis-a, cluster: euw1a, label: redis, kind: cache, tech: Redis 7, replicas: 1 }
- { id: ekafka-a, cluster: euw1a, label: kafka broker 1, kind: queue, tech: Kafka 3.7, replicas: 1 }
- { id: epg-a, cluster: euw1a, label: postgres replica, kind: store, tech: Postgres 16, replicas: 1 }
- { id: eredis-b, cluster: euw1b, label: redis, kind: cache, tech: Redis 7, replicas: 1 }
- { id: ekafka-b, cluster: euw1b, label: kafka broker 2, kind: queue, tech: Kafka 3.7, replicas: 1 }
- { id: epg-b, cluster: euw1b, label: postgres cascading replica, kind: store, tech: Postgres 16, replicas: 1 }
- { id: eredis-c, cluster: euw1c, label: redis, kind: cache, tech: Redis 7, replicas: 1 }
- { id: ekafka-c, cluster: euw1c, label: kafka broker 3, kind: queue, tech: Kafka 3.7, replicas: 1 }
edges:
- epg-a --> epg-b: cascade
- ekafka-a -> ekafka-b: partition replicas
- ekafka-b -> ekafka-c: partition replicas
- ekafka-c -> ekafka-a: partition replicas
```
## Copies of each part
The counts below are the steady-state minimum. Autoscaling raises `orders-api` and `orders-worker` above these numbers under load but never below them, and it never changes the stateful counts.
```table
title: Replica counts per part
columns:
- Part
- { label: Per zone (us-east-1), align: r }
- { label: us-east-1 total, align: r }
- { label: Per zone (eu-west-1), align: r }
- { label: eu-west-1 total, align: r }
- { label: All regions, align: r, highlight: true }
- Placement rule
rows:
- [{ v: gateway, lead: true }, 2, 6, 2, 6, { v: 12, highlight: true }, One Envoy pair per zone behind the regional ALB]
- [{ v: orders-api, lead: true }, 4, 12, 2, 6, { v: 18, highlight: true }, Spread evenly; pod anti-affinity forbids two pods on one node]
- [{ v: orders-worker, lead: true }, 2, 6, 1, 3, { v: 9, highlight: true }, Consumer group per region; partitions rebalance on zone loss]
- [{ v: Redis, lead: true }, 1, 3, 1, 3, { v: 6, highlight: true }, One node per zone; cache only, no persistence]
- [{ v: Kafka broker, lead: true }, 1, 3, 1, 3, { v: 6, highlight: true }, "Replication factor 3, min.insync.replicas 2"]
- [{ v: Postgres, lead: true }, 1, 3, { v: "1 (zones a, b)", tone: muted }, 2, { v: 5, highlight: true }, "Primary in us-east-1a; sync standby in b, async in c; two async replicas in eu-west-1"]
note: "Totals count running instances, not nodes. A zone outage in us-east-1 leaves 8 orders-api copies, 2 Kafka brokers in sync, and a Postgres standby ready to promote."
```