Active-Active Multi-Region Design in Kafka

Active-Active Multi-Region Design in Kafka

How to run Kafka in two or more regions at the same time, let applications read and write in every one of them, survive the loss of a whole region, and stay honest about what can and cannot be guaranteed when the data has to cross an ocean.

Picture an online shop with customers in Mumbai, Singapore and Frankfurt. On a normal day, every customer wants the page to load fast, the cart to save instantly, and the order to be safe. Now imagine that the building that holds the shop’s servers in Singapore loses power. If the shop lives only in Singapore, every customer everywhere sees an error page. If the shop lives in two places and one of them is just a quiet copy waiting for a disaster, the owner is paying for machines that do nothing on ordinary days, and nobody is really sure the copy will work when the disaster arrives.

There is a third way. Run the shop in every region at the same time. Every region takes orders, every region keeps its own data, and the regions keep telling each other what happened. If one region disappears, the others already know almost everything and simply carry on. This is called an active-active design. It is powerful, and it is also one of the trickiest things you can build, because the moment two places can change the same information at the same time, you must decide what “correct” means. This guide explains, from the very beginning, how to build it with Apache Kafka: the two main ways to spread Kafka over regions, the tools that copy data, how progress markers are translated, how conflicts are handled, what breaks, and how to test it before it breaks for real.

1What Is Active-Active Multi-Region Design?

The plain-English idea first. The machinery follows in later chapters.

A region is a geographic area that holds one or more data centers, such as Mumbai, Singapore or Frankfurt. Cloud providers sell regions as separate places with separate power, cooling and networks. Two regions are far enough apart that a flood, a fire or a bad software update in one is very unlikely to touch the other.

A multi-region design is any system that keeps running parts, or copies of its data, in more than one region. There are two big families. In an active-passive design, one region does all the work and the other waits with a copy of the data. In an active-active design, every region does real work at the same time: it accepts new data from local applications, serves local readers, and shares its data with the other regions.

Everyday analogy

Think of a chain of bakeries in different cities. In an active-passive chain, only the main bakery sells bread, and a second shop stays closed with the same recipes in case the main one burns down. In an active-active chain, every bakery sells bread every day, and every evening they phone each other to share what sold and what recipes changed. If one shop closes, customers walk into the next one, and it already knows the recipes. The hard part is the phone calls: what if two shops changed the same recipe on the same afternoon?

What Kafka brings to the picture

Apache Kafka is a system that stores streams of events, such as “order placed” or “payment received”, in an ordered log, and lets many programs read them. Kafka was designed to run inside one data center or one region, where machines are close together and the network is fast and reliable. Spreading it over regions means dealing with slower links, longer delays and the chance that the link between regions breaks while both sides stay alive.

That single fact, that the link between regions can be slow or broken while both regions are healthy, drives almost every decision in this guide.

1-2 ms
typical round trip between machines in one region (illustrative)
50-200 ms
typical round trip between distant regions (illustrative)
RPO > 0
data that may be lost when copying between clusters is asynchronous
i
Two words to remember

RPO (recovery point objective) is how much recent data you can afford to lose in a disaster. RTO (recovery time objective) is how long you can afford to be down. Every multi-region choice is a trade between these two numbers, cost, speed and complexity.

2Kafka Basics You Need First

A short tour of the parts of Kafka that the rest of this guide keeps pointing at.

If you already know Kafka well, skim this chapter. If not, these few ideas are all you need. Each has a small card below, and the paragraphs after the cards show how the ideas fit together.

Server

Broker

A machine that runs Kafka and stores data. A group of brokers working together is a cluster.

Stream

Topic

A named stream of events, such as orders or payments. Think of it as a labelled folder that only grows.

Slice

Partition

A topic is split into partitions so many machines can share the load. Order is guaranteed only inside one partition.

Position

Offset

The number of a record inside a partition: 0, 1, 2 and so on. It is the record’s address.

Copy

Replication

Each partition is copied to several brokers inside one cluster, so a broker failure loses nothing.

Writer

Producer

A program that sends records to a topic. It chooses the partition, often by hashing the record’s key.

Reader

Consumer

A program that reads records and remembers how far it has read by saving an offset.

Team

Consumer group

Consumers that share a topic’s partitions. Each group keeps its own saved offsets.

Replication inside a cluster versus between clusters

Kafka copies each partition to several brokers. One broker is the leader and takes all writes. The others are followers that copy the leader. The copies that are keeping up are called the in-sync replicas, or ISR. A producer can ask the leader to wait until all in-sync copies have the record before saying “done”. This setting is called acks=all, and together with a minimum in-sync count it is what makes a single cluster safe.

That copying is fast and tightly controlled because it happens inside one cluster over a fast network. Copying between clusters is a different job, done by a different tool, over a slower network, and with weaker promises. The whole art of multi-region Kafka is understanding this difference.

Offsets belong to a cluster

An offset only has meaning inside one partition of one cluster. Record number 5,000 in the orders topic of the Mumbai cluster has nothing to do with record number 5,000 in the copy of that topic in the Singapore cluster. They may hold different records, because the copying tool may skip, retry, or interleave data. This detail causes many of the surprises you will see later, especially when consumers have to move from one region to another.

!
A common beginner trap

Never assume that an offset saved in one cluster can be used as it is in another cluster. It usually needs to be translated, and the translation is approximate.

3Why Regions Matter: Speed, Survival and the Law

Three very different pressures push teams toward more than one region. Knowing which one is yours changes the design.

Nobody builds multi-region systems for fun. The cost in money and effort is high. Teams do it because one of three pressures leaves no better choice.

Pressure one: distance is slow

Light travels through fibre at roughly two thirds of its speed in a vacuum. A signal from Mumbai to Frankfurt cannot arrive in under about 60 milliseconds one way, no matter how much money you spend. If a customer in Frankfurt has to wait for a round trip to Mumbai on every click, the app feels sluggish. Serving that customer from a nearby region is the only cure, and that means the data must live nearby.

Pressure two: whole regions fail

Regions fail less often than single machines, but they do fail. A bad configuration change, a power problem, a cooling failure, a cut cable or a software bug in the cloud provider’s own control system can make a region unreachable for minutes or hours. Companies with strict uptime promises cannot say “our provider was down”. They need a second region ready to carry the traffic.

Pressure three: laws about where data lives

Some laws and contracts require that data about citizens of a country, or customers of a certain kind, stays inside a geographic boundary. This is called data residency. Multi-region designs must keep such data in the right regions and share only what is allowed, such as anonymous totals.

PressureWhat you really needDesign direction
Speed for far-away usersLocal reads and local writesActive-active with regional ownership of data
Survive a region outageA ready second region with recent dataActive-passive is often enough; active-active gives faster recovery
Data residency rulesData that never leaves a boundarySeparate regional clusters with selective, filtered replication
All three togetherLocal speed, survival and legal controlActive-active with strict topic design and careful replication

Production example: the global ride app

A ride-hailing company keeps a Kafka cluster in every major region so that a driver’s location updates stay close to the drivers and riders that use them. Trips that start in one region are finished in that region. Some data, such as pricing models and fraud signals, is copied between regions so that every region can make good decisions. When one region has trouble, traffic shifts to another region that already holds recent copies of what it needs. The lesson: most events have a natural home region, and the design gets far simpler when you let them keep it.

4Deployment Models: Single Region, Active-Passive, Active-Active

Three ways to lay out Kafka, from simplest to hardest, with what each one buys you.

Before choosing active-active, compare it fairly with the other layouts. Many teams discover that a simpler layout meets their real needs.

flowchart TB
  subgraph SR["Single Region"]
    A1["Apps"] --> K1["Kafka Cluster
3 zones"] end subgraph AP["Active-Passive"] A2["Apps in Region A"] --> K2["Kafka A
active"] K2 -- "one-way copy" --> K3["Kafka B
standby"] A3["Apps in Region B
idle until failover"] -.-> K3 end subgraph AA["Active-Active"] A4["Apps in Region A"] --> K4["Kafka A"] A5["Apps in Region B"] --> K5["Kafka B"] K4 -- "copy" --> K5 K5 -- "copy" --> K4 end
Figure 1. Three layouts. The arrows between clusters show which way data is copied. In active-active, the copy runs both ways.

The diagram shows the three layouts side by side. In the single-region layout, one cluster spreads its brokers across several zones inside a region. It survives the loss of a machine or a zone, but not the loss of the region. In the active-passive layout, a second cluster receives a one-way copy and stays quiet until a disaster. In the active-active layout, both clusters take writes, and each one copies to the other.

QuestionSingle regionActive-passiveActive-active
Survives a machine or zone lossYesYesYes
Survives a region lossNoYes, after failoverYes, often with little pause
Data lost in a region loss (RPO)Everything since the last backupWhatever was not yet copiedWhatever was not yet copied
Recovery time (RTO)Hours or moreMinutes to an hour, needs a switchSeconds to minutes, traffic already there
Speed for far-away usersPoorPoor, all writes go to one regionGood, users write locally
Conflicts possibleNoNoYes, must be designed for
Cost1xAbout 2xAbout 2x, but the second half does useful work
Operational difficultyLowMediumHigh
!
A standby you never test is not a standby

Many active-passive systems fail on the day they are needed, because nobody ever sent real traffic to the passive side. One quiet advantage of active-active is that both sides are proven every day.

5Option A: One Cluster Stretched Across Regions

The first way to use two regions: pretend they are one big cluster. It is elegant and it is expensive in delay.

In a stretch cluster, the brokers of a single Kafka cluster are placed in several regions or data centers. Kafka does not know or care that some brokers are far away. Each partition has replicas in different locations, and the normal replication rules keep them in step. Kafka’s own rack awareness feature lets you label each broker with its location, so that the replicas of a partition are spread across locations.

flowchart LR
  subgraph R1["Location 1"]
    B1["Broker 1
leader of P0"] B2["Broker 2"] end subgraph R2["Location 2"] B3["Broker 3
follower of P0"] B4["Broker 4"] end subgraph R3["Location 3"] B5["Broker 5
follower of P0"] Q["Metadata quorum
voter"] end P["Producer"] --> B1 B1 -- "replicate" --> B3 B1 -- "replicate" --> B5 C["Consumer"] --> B1
Figure 2. A stretch cluster. One partition has copies in three locations. A write is only acknowledged after the copies agree, so every write waits for the slowest required link.

What you gain

Replication is synchronous, meaning the producer is told “done” only after enough copies exist. If the leader’s location vanishes, another location already holds every acknowledged record, so RPO is zero for acknowledged writes. Applications see one cluster, one set of topics, one set of offsets. Consumers do not need offset translation, because the offsets are the same everywhere. There is no second tool to run, and no naming tricks.

What you pay

Every write with acks=all waits for followers in other locations. If the round trip between locations is 80 milliseconds, every write takes at least that long. Throughput per producer drops, and tail latency rises when the link is congested. Cross-region traffic is also billed by cloud providers, and a stretch cluster sends every byte across regions at least twice for replication.

There is a second, quieter cost: quorum. The cluster’s metadata controllers must agree by majority. With only two locations, losing the location that holds the majority stops the cluster from making decisions, even though the other location is healthy. That is why serious stretch clusters use three locations, where losing any one leaves a majority.

Everyday analogy

A stretch cluster is like a group of friends in three cities who must all say “yes” over the phone before anyone signs a contract. Nothing is ever lost, because everyone agrees first. But every signature takes as long as the slowest phone line, and if two of the three cities lose their connection, nobody can sign anything.

Benefits

  • No data loss for acknowledged writes when one location fails.
  • One set of topics and offsets, so no translation and no naming rules.
  • Automatic failover using normal leader election.
  • Simple for applications, since they see a single cluster.

Trade-offs

  • Every write pays the cross-region round trip.
  • Needs three locations for a safe quorum, and a very stable network between them.
  • A network split can stop writes for the whole cluster.
  • A cluster-wide software problem hits every region at once.
i
When a stretch cluster is a good idea

It fits best when the locations are close together, for example three data centers in one metropolitan area with a round trip of a few milliseconds. Across continents, it is almost always the wrong tool.

6Option B: Separate Clusters Connected by Replication

The second way: every region owns a full cluster, and a copying tool moves data between them. This is what most active-active systems use.

In the multiple-cluster design, each region runs its own independent Kafka cluster. Applications talk only to the cluster in their own region. A separate tool reads records from one cluster and writes them into the other. Because the copying happens after the local write has already succeeded, it is asynchronous: the local application is never slowed down by the distance.

flowchart LR
  subgraph RA["Region A"]
    PA["Producers A"] --> KA["Kafka Cluster A"]
    KA --> CA["Consumers A"]
  end
  subgraph RB["Region B"]
    PB["Producers B"] --> KB["Kafka Cluster B"]
    KB --> CB["Consumers B"]
  end
  KA -- "replicator A to B" --> KB
  KB -- "replicator B to A" --> KA
Figure 3. Two independent clusters with a replicator in each direction. Local traffic never waits for the far region.

Why this is the common choice

Local writes stay fast, because a producer in Region A only waits for its own cluster. Failures are contained: a broken cluster in Region B does not stop Region A from taking writes. Each cluster can be upgraded on its own schedule, which limits the damage of a bad release. And each region can keep some topics private, replicating only what is allowed.

What you give up

Because copying is asynchronous, there is always a delay between the moment a record is written in Region A and the moment it appears in Region B. That delay is usually well under a second in a healthy system, but it can grow to minutes or hours during trouble. If Region A is destroyed, the records that had not yet been copied are lost. That is why RPO is greater than zero. You must also handle topic naming, offset translation, duplicates and conflicts yourself, which are the topics of the next chapters.

PropertyStretch clusterSeparate clusters
Copy styleSynchronous inside KafkaAsynchronous by an outside tool
Write latencyIncludes the region round tripLocal only
Data loss on region failureNone for acknowledged writesData not yet copied
OffsetsSame everywhereDifferent in every cluster
Failure blast radiusOne cluster spans everythingContained per region
Best forNearby sites, zero data lossFar-apart regions, low latency
Local writes stay fast when the far copy is allowed to be a little late.

7The Replication Tools: MirrorMaker 2, Cluster Linking and Others

Several tools can move data between Kafka clusters. They differ in what they copy, how faithfully, and who supports them.

A replicator is a program that reads records from a source cluster and writes them to a target cluster. It sounds simple, and at small scale it is. At large scale, it must also handle topic creation, partition counts, configuration, access rules, consumer positions, restarts and loop prevention. Over the years, several tools have grown to cover these jobs.

Open source

MirrorMaker 2

Part of Apache Kafka. Built on Kafka Connect. Copies data, topic settings, access rules and consumer group positions. The default choice for open-source setups.

Vendor

Cluster Linking

A feature of some commercial Kafka platforms. The target cluster pulls data directly from the source and keeps the same offsets, producing read-only mirror topics.

Vendor

Replicator connector

A commercial Kafka Connect based tool that copies topics between clusters and can translate consumer positions.

Legacy

MirrorMaker 1

The first Kafka mirroring tool. Simple consume-then-produce. It has been replaced by version 2 and should not be used for new designs.

In-house

Company tools

Large companies have built their own, such as uReplicator at Uber and Brooklin at LinkedIn, to solve scale and stability limits they hit with early tools.

Custom

Your own app

A Kafka Streams or consumer-producer application can copy selected topics with filtering or masking. Useful for special cases, expensive to maintain.

The most important difference: offsets

Classic mirroring tools, including MirrorMaker 2, read records and write them again on the target. The target cluster assigns new offsets, so record 5,000 on the source may become record 4,982 on the target. Byte-for-byte linking tools copy the log itself and keep the offsets identical. Identical offsets make consumer failover far simpler, but they also mean the mirror topic is read-only and must be promoted before applications can write to it. That does not fit a design where both regions must write into the same topic name, so active-active designs still use separate topics per region, which the next chapters explain.

i
Choose by capability, not by fashion

Ask three questions. Do you need open source only? Do you need identical offsets? Do you need to transform or filter records on the way? The answers point to the tool. This guide uses MirrorMaker 2 for its examples because it is part of Apache Kafka and its ideas apply to the others.

8Inside MirrorMaker 2

MirrorMaker 2 is a set of connectors that run inside Kafka Connect. Each connector has one small job.

Kafka Connect is a framework that runs connectors, which are plug-in programs that move data in and out of Kafka. It handles restarts, scaling across workers and storing its own progress. MirrorMaker 2 (MM2) is a family of three connectors that ride on top of this framework.

flowchart LR
  subgraph SRC["Source cluster A"]
    T1["orders topic"]
    G1["Consumer group positions"]
    TA["Topic configs and ACLs"]
  end
  subgraph MM["MirrorMaker 2 on Kafka Connect"]
    MS["MirrorSourceConnector
copies records"] MC["MirrorCheckpointConnector
translates group offsets"] MH["MirrorHeartbeatConnector
sends heartbeats"] end subgraph TGT["Target cluster B"] T2["A.orders mirror topic"] OS["offset-syncs topic"] CP["A.checkpoints.internal"] HB["heartbeats topic"] end T1 --> MS --> T2 MS --> OS G1 --> MC OS --> MC --> CP MH --> HB TA --> MS
Figure 4. The three MirrorMaker 2 connectors and what each one writes. Names are typical; the exact topic names depend on configuration.

The three connectors

ConnectorJobWhy it matters
MirrorSourceConnectorReads records from source topics and writes them to the target. Also creates matching topics, keeps partition counts in step, and can copy topic settings and access rules.This is the data pipe. Its speed sets your replication lag.
MirrorCheckpointConnectorReads consumer group positions from the source and, using the offset-sync records, works out the matching positions on the target. It can write them to a checkpoints topic and optionally apply them to groups.This is what makes consumer failover practical.
MirrorHeartbeatConnectorSends a small heartbeat record at regular intervals.It proves the whole chain works and lets you measure end-to-end delay even when no real data flows.

How offset syncs work

Every so often, the source connector records a pair of numbers: “record 5,000 on the source became record 4,982 on the target”. These pairs go into an internal topic. The checkpoint connector uses them like landmarks on a map. When it sees that a group has read up to source offset 5,010, it finds the nearest landmark at or before that point and computes a safe target offset. Because landmarks are sparse, the answer is slightly behind. That is deliberate: it is better to re-read a few records than to skip any.

Running it

MM2 can run in three ways: as a dedicated cluster using its own driver program, as connectors on an existing Kafka Connect cluster, or in a standalone mode for tests. For production, teams usually run a dedicated Connect cluster in the target region, so that the process reads across the long link and writes locally. Reading remotely and writing locally tends to be more forgiving, because a remote read that times out can simply be retried, while a remote write that times out may leave doubt about whether the record arrived.

Everyday analogy

MM2 is a courier company with three staff. One courier carries parcels between two warehouses. A second keeps a notebook that translates the address labels of one warehouse into the address labels of the other. A third sends a postcard every minute to prove the road is open. If the notebook keeper is late, customers can still get parcels, but they may not find their old place in the shelves.

9Internal Working: How the Source Connector Copies Records

A closer look at the engine room, since its behaviour explains most lag and duplicate stories.

The source connector splits its work into tasks. Each task is assigned a set of source partitions. A task acts like an ordinary consumer for those partitions and like an ordinary producer for the target. It has no group of its own that Kafka rebalances in the usual way. Instead, Kafka Connect hands out the partitions and stores each task’s progress in its own internal offsets storage.

Partition mapping

MM2 writes each record to the same partition number on the target that it came from on the source. That is why the partition counts of the source and mirrored topics must match: key-to-partition placement stays the same, and order inside every partition is kept. If someone adds partitions to the source topic later, the connector notices on its next refresh and adds partitions to the mirror. Records with the same key can then move to a different partition on the source side, and the mirror will follow, but old and new records for that key are split between partitions in both places.

Progress and restarts

The connector commits its progress at intervals. The commit says, “I have safely written everything up to source offset N”. If it crashes between writing a batch and committing, it restarts from the last commit and copies part of the batch again. This is the exact mechanism that produces duplicates. Shorter commit intervals make duplicates rarer and smaller, at the cost of more bookkeeping traffic.

Headers, keys and timestamps

The copier keeps the record’s key, value, headers and, in normal settings, its original timestamp. This is useful: a reader in the target region can compute how old a record is, and windowed jobs based on event time keep working. Log-append timestamps, set by the broker, are not preserved, because the target broker assigns its own.

Everyday analogy

Imagine a mail sorter who moves letters from one post office to another and writes the last letter number moved on a sticky note each hour. If the sorter faints halfway through the hour, the replacement reads the old sticky note and re-moves some letters. Nothing is lost, but a few letters arrive twice.

i
Lag has three usual suspects

When replication falls behind, check first for under-sized workers or too few tasks, then for a slow or lossy network link with small buffers, and then for throttling by broker quotas on either cluster.

10Topic Naming and Preventing Infinite Loops

If both regions copy everything to each other, a record could bounce back and forth forever. One naming rule stops that.

Imagine Region A copies the orders topic to Region B, and Region B copies the orders topic back to Region A. A record written in Region A is copied to B. B’s copier sees a new record in its orders topic and copies it back to A. A’s copier sees it and copies it to B again. The record circles until the disks fill up. This is a replication loop, and it is the most dangerous mistake in active-active Kafka.

The prefix rule

MM2’s default answer is to rename topics when it copies them. A topic called orders in Region A appears in Region B as a.orders, where a is the alias of the source cluster. The copier in Region B is told to copy only topics that do not already carry a prefix from the other region. So a record created locally in A, copied to B as a.orders, is not copied back, because a.orders is recognised as a remote topic that came from A. Loops become impossible by design.

flowchart LR
  subgraph A["Cluster A"]
    AO["orders
local writes"] ABO["b.orders
copy from B"] end subgraph B["Cluster B"] BO["orders
local writes"] BAO["a.orders
copy from A"] end AO -- "copied as a.orders" --> BAO BO -- "copied as b.orders" --> ABO ABO -. "not copied back" .-> X["stop"] BAO -. "not copied back" .-> X
Figure 5. Prefixes prevent loops. Each cluster has one local topic that applications write to, and one remote topic per other region, which applications only read.

What this means for applications

Producers in every region write to the plain orders topic in their own cluster. To see the whole world, a consumer subscribes to a pattern that matches orders and every prefixed version of it. A consumer in Region A then reads its own orders plus b.orders. Each topic has a single writer region, which is a very important property: within any one topic, only one region ever writes. This makes ordering, ownership and debugging much easier.

!
The identity policy trap

MM2 offers a replication policy that keeps the topic name unchanged. It is tempting because applications do not need patterns. But it removes the automatic loop protection. Use it only for one-way copies, never for two-way active-active copying, unless you add your own protection.

i
Design tip: a stable alias

Choose short, permanent cluster aliases such as the region code. The alias becomes part of every topic name, every dashboard and every consumer configuration. Renaming it later means renaming everything.

11Data Flow and Lifecycle: One Record Across Two Regions

Follow a single order from the moment it is written in Mumbai to the moment a reader in Singapore sees it.

sequenceDiagram
  participant P as Producer in Region A
  participant KA as Kafka A
  participant MM as MirrorMaker 2
  participant KB as Kafka B
  participant C as Consumer in Region B
  P->>KA: 1. Write order 1001 to orders
  KA-->>P: 2. Acknowledge after local replicas agree
  MM->>KA: 3. Fetch new records
  KA-->>MM: 4. Return order 1001
  MM->>KB: 5. Write order 1001 to a.orders
  KB-->>MM: 6. Acknowledge
  MM->>KB: 7. Record an offset sync pair
  C->>KB: 8. Fetch from a.orders
  KB-->>C: 9. Return order 1001
Figure 6. The life of one record. The producer is finished at step 2. Steps 3 to 9 happen later and may be delayed by minutes if the link is slow.
1

Local write

The producer sends the order to its own cluster. The cluster copies it to enough local brokers and answers. From the producer’s point of view, the job is done.

2

Pickup by the replicator

MM2 keeps fetching from the source topic like any consumer. It sees the new record within milliseconds when it is caught up.

3

Cross-region hop

The record travels over the long link. This is where most of the delay lives, and where cloud transfer charges start.

4

Remote write

MM2 writes the record into the prefixed topic in the target cluster. It gets a new offset there.

5

Progress is recorded

MM2 remembers how far it has copied and periodically stores an offset sync pair for later translation.

6

Remote read

Consumers in the target region read the mirrored topic as if it were any other. They see the record slightly after the original writer finished.

Where the record can go wrong

Between steps 3 and 5, the copier might crash after writing the record but before saving its progress. On restart, it copies the record again, and now the target holds it twice. This is the source of duplicates, discussed shortly. If the source cluster is destroyed between steps 2 and 3, the record is lost from the world even though the producer was told it was safe. This is the replication gap, the reason RPO is not zero.

Under 1 s
typical replication lag in a healthy, well-sized setup (illustrative)
Minutes
lag seen when the link is congested or workers are undersized
Hours
backlog after a long outage of the link, drained at the copier’s maximum speed

12Consumer Offsets Across Regions

When a consumer moves to another region, it must find its place again. Offset translation is the map for that.

A consumer group remembers its progress as a saved offset for every partition. If a whole application moves from Region A to Region B, the saved offsets in A’s cluster are useless in B, because B’s copy of the topic has different offsets. Without help, the application has two bad choices: start from the newest records and skip data, or start from the beginning and re-process everything.

flowchart TD
  S["Consumer group reads a.orders
saved position in Cluster A"] --> CK["Checkpoint connector reads group position"] CK --> LM["Finds nearest offset sync landmark"] LM --> TR["Computes translated offset for Cluster B"] TR --> W["Writes checkpoint to the checkpoints topic"] W --> O{"Sync group offsets
enabled?"} O -- "yes" --> AP["Applies offsets to the same group name in Cluster B automatically"] O -- "no" --> MAN["Failover script reads checkpoints and sets offsets"] AP --> R["After failover the group resumes near its old place"] MAN --> R
Figure 7. How a consumer group’s position is carried to the other cluster. The result is approximate and slightly behind, so a few records may be read twice.

Two ways to use the translated offsets

Automatic: newer Kafka versions can apply translated offsets straight into the target cluster’s group, as long as that group has no active members there. When the application starts in the target region, it simply resumes. Manual: the checkpoints topic is read by a failover script or by the application itself, which then sets its own starting positions. The manual route gives more control, and the automatic route gives less work at the worst possible moment.

Things that surprise people

  • Translation only covers groups that consumed the mirrored topics. A group that reads the local topic in Region A is translated for the mirrored copy of that topic in Region B, which is where the group would go after failover.
  • A group that is active in both clusters at once with the same name can fight the automatic sync. Keep group names clear about where they run, or make sure only one side is active for each group.
  • The translated position lags behind the real one by up to one landmark gap. Expect a small burst of duplicates after failover, and design consumers so that repeats are harmless.
  • Checkpoints are written on a timer. If the region dies just after a big burst of progress, the last checkpoint may be a few seconds old.
!
Never use the “latest” shortcut during failover

Starting a moved consumer at the latest offset feels quick and safe. It silently skips every record that was still being copied when the disaster struck. Use translated offsets, and accept the duplicates.

13Ordering, Duplicates and Delivery Guarantees

What Kafka promises inside one cluster is stronger than what you get across regions. Knowing the difference prevents nasty surprises.

Inside a single cluster, Kafka guarantees that records in one partition keep their order, and with the right settings, that acknowledged records are not lost when a broker fails. With transactions, a group of writes can succeed or fail together. Across regions, these promises weaken.

PropertyInside one clusterAcross regions with async copying
Order within a partitionGuaranteedPreserved for the mirrored topic of one source topic, since MM2 keeps the same partition mapping and order
Order across partitionsNot guaranteedNot guaranteed
Order between regionsNot applicableNot defined. Region A’s records and Region B’s records interleave differently in different places
Loss on failureNot for acknowledged records with proper settingsPossible for records not yet copied
DuplicatesAvoidable with idempotent producers and transactionsPossible after copier restarts; the copy path is generally at-least-once
TransactionsAtomic groups of writesRecords are copied one by one; the atomic grouping is not preserved as a unit on the other side

The consequence: make consumers idempotent

An operation is idempotent when doing it twice has the same effect as doing it once. Setting a customer’s address to a given value is idempotent. Adding 100 rupees to a balance is not. Since duplicates can appear on the far side, and since consumers moving between regions re-read a few records, the reliable defence is to make the consuming code tolerate repeats. The usual method is to give every event a unique event ID and to remember the IDs already handled, or to write results with an operation that overwrites by key.

Everyday analogy

Duplicates are like a letter that the post office delivers twice. If the letter says “the meeting is at 5 pm”, nobody minds. If it says “please send me 100 rupees”, the second copy is a problem. Writing a reference number on every letter lets the receiver say, “I already dealt with number 4711”.

i
Exactly-once stops at the cluster boundary

Kafka’s exactly-once features protect work inside one cluster. They do not stretch over an asynchronous copy to another cluster. Plan for at-least-once delivery across regions and add idempotency at the edges.

14Conflicts: When Two Regions Change the Same Thing

The heart of active-active difficulty. Copying is easy. Agreeing is hard.

A conflict happens when two regions make different changes to the same piece of information before either has heard about the other’s change. Suppose a customer’s shipping address is updated from the phone app, handled in Region A, and at almost the same moment from the website, handled in Region B. Each region writes its new address locally. Then the replicators carry each change to the other side. Now each region holds both updates, but they arrive in opposite order. Which address wins?

sequenceDiagram
  participant A as Region A
  participant B as Region B
  Note over A,B: Both start with address Old
  A->>A: Set address to Street 1 at 10:00:00.100
  B->>B: Set address to Street 2 at 10:00:00.200
  A-->>B: Replicate Street 1
  B-->>A: Replicate Street 2
  Note over A: Sees Street 1 then Street 2
  Note over B: Sees Street 2 then Street 1
  Note over A,B: Without a rule, A ends with Street 2 and B ends with Street 1
Figure 8. A classic conflict. Both regions applied “latest arrival wins”, so they ended with different answers. Regions that disagree forever are a serious bug.

Strategies for resolving conflicts

Avoid

Single home region

Give every record one owner region. Only the owner may change it. Other regions send requests to the owner. There is nothing to conflict.

Rule

Last writer wins

Compare timestamps carried in the record and keep the newest. Simple, but depends on clocks and silently discards the other change.

Rule

Version numbers

Each change carries the version it was based on. A change based on an old version is rejected or merged, instead of blindly overwriting.

Math

Mergeable data types

Use data whose changes can be combined in any order, such as counters that only add or sets that only grow. Also called CRDTs.

Human

Manual review

Flag the conflict and let a person or a business rule decide. Suitable for rare, high-value cases.

The cleanest approach is always avoidance. If a record can only be changed in one region, no conflict can exist. The next chapter shows how to build that, because it is the pattern behind most successful active-active systems.

!
Clocks lie

“Last writer wins” depends on comparing times from different machines. Clocks drift by milliseconds or more, and a misconfigured machine can be minutes off. Use it only where losing an occasional update is acceptable, and prefer logical version numbers where correctness matters.

15Key Ownership and Home-Region Routing

The most reliable way to avoid conflicts is to make sure every piece of data has exactly one place where it may change.

In a home-region design, each entity, such as a customer, an account or a device, is assigned to one region. That region is its home. All changes to that entity are written by applications running in the home region, into the home region’s local topic. Other regions receive those changes through replication and treat them as read-only facts.

flowchart TD
  U["User request arrives
at nearest region"] --> R{"Is this user's
home region local?"} R -- "yes" --> L["Handle locally
write to local orders topic"] R -- "no" --> F["Forward request to home region
or write to a request topic"] F --> H["Home region handles it
writes to its local topic"] L --> RP["Replicator copies to other regions
as prefixed topics"] H --> RP RP --> V["Every region builds its read view
from all regional topics"]
Figure 9. Home-region routing. Writes for an entity always end up in its home region’s topic, so two regions never write the same entity.

How the home is chosen

The home can be based on where the customer lives, which is the natural choice and gives the lowest delay for most requests. It can be based on a hash of the customer ID, which spreads load evenly but may put a customer far from their home. Or it can come from a small lookup table that says which region owns which customer range. Whatever the rule, it must be stable: if two regions disagree about a customer’s home, conflicts return.

What happens when the home fails

If a home region is down, its customers cannot change their data unless another region takes over. Takeover means updating the ownership table, so that another region becomes the home. This must be a deliberate, coordinated step, ideally through a small strongly consistent system such as a metadata store, and not a decision each region makes alone. Otherwise, two regions may both believe they are the new owner. Some teams accept a short read-only period for affected customers, rather than risk a split.

Everyday analogy

A big company has branch offices, and every customer file belongs to one branch. If you walk into a different branch, the clerk reads your file from a shared copy but phones your home branch to change anything. Nobody ever writes into the same file from two places. If your home branch closes, head office formally hands your file to another branch, and only then can it be changed.

Production example: banking and messaging

Large messaging and banking platforms commonly pin an account or conversation to a home region and route all writes there. Reads are served from the nearest replicated copy. The pattern gives fast local reads and safe single-writer updates, and accepts that a remote user’s write pays one extra hop. The lesson: it is usually far cheaper to route a write than to merge two writes.

16Consumer Patterns: Local, Aggregate and Global Views

Once data lives in several prefixed topics, consumers need a plan for what to read. Three plans cover most needs.

Pattern 1

Local only

Read only the local topic. Simple and fastest, but the application sees only data created in its own region. Good for region-specific work such as local fulfilment.

Pattern 2

Aggregate view

Read the local topic plus every remote prefixed topic and build a combined view. Every region ends up with a full picture, slightly delayed. The most common active-active reader.

Pattern 3

Central aggregate cluster

Copy every region’s topics into one extra cluster and let global analytics read there. Keeps heavy readers away from the busy regional clusters.

Building the aggregate view

A consumer with the aggregate pattern subscribes to a name pattern that matches the local topic and every mirrored copy. All the records arrive in one stream, but they come from different partitions and different regions, so the consumer must not assume any global order. If it needs an order, it should sort by an event time or a version number carried inside the record, and accept that late records can still arrive.

The same-shape rule

The local and mirrored topics must contain the same kind of record, with the same schema. That means schemas need to be shared between regions and evolved carefully, which is covered in the supporting systems chapter. If one region starts writing a new field that others cannot read, every aggregate reader breaks at the same moment.

i
Add the origin to every record

Put a small field in every event that names the region where it was created, plus a unique event ID and an event time. These three fields make debugging, deduplication and conflict handling far easier, and they cost almost nothing.

PatternFreshness of remote dataLoad on regional clustersBest for
Local onlyNone, remote data not readLowestRegional processes
Aggregate viewDelayed by replication lagMediumUser-facing apps that need the whole world
Central aggregate clusterDelayed by two hopsLow on regions, high on the central oneAnalytics, reporting, machine learning

17Kafka Streams and Stateful Applications in Active-Active

Programs that keep running totals hold their own state. In two regions, that state can drift apart unless you plan for it.

Kafka Streams is a library for building applications that read topics, transform events, keep state such as counts or balances, and write results. The state lives in local stores backed by internal Kafka topics. In an active-active design, a Streams application usually runs in every region, each with its own state stores and internal topics in its own cluster.

The drift problem

Suppose each region runs a job that counts orders per product. Each job reads its local orders topic and the remote prefixed topic. If both jobs read exactly the same records, they end with the same counts. But the two jobs read the records in different orders, at different times, and after a failover may re-read some records. If the counting logic is not designed for this, the two answers slowly disagree.

Kind of logicOrder sensitive?Safe across regions?
Counting distinct event IDsNoYes, if duplicates are removed by ID
Summing amounts with dedup by event IDNoYes
Keeping the latest value by event time or versionHandled by the ruleYes, if the rule is deterministic
Running balance where each event depends on the previous stateYesOnly if a single region owns each account
Detecting sequences such as “A then B”YesRisky, arrival order differs between regions

Practical rules

  • Prefer logic where the result does not depend on arrival order.
  • Remove duplicates by event ID inside the job, with a store that expires old IDs after a safe period.
  • Give every application its own application ID in each cluster and never mix internal topics between regions.
  • Do not replicate internal changelog and repartition topics as a substitute for running the job in both regions. Restoring from mirrored changelogs across different offsets is fragile.
  • Where order matters, let one region own the key, and let the other regions only read results.
!
Exactly-once does not follow the data

Kafka Streams’ exactly-once mode covers reading, state and writing inside one cluster. Once records cross to the other cluster by an asynchronous copy, that guarantee ends. Downstream jobs in the other region must protect themselves with idempotent design.

18Supporting Systems: Schemas, Connectors, Access Rules and Configs

Kafka is rarely alone. Everything around it must also work in more than one region, or the design has a hidden single point of failure.

Schema registry

A schema describes the shape of a record, such as its field names and types. A schema registry stores schemas and gives each one an ID that is embedded in every record. A reader looks up the ID to know how to decode the bytes. If Region B receives a record that carries schema ID 42 but its own registry has never heard of ID 42, the reader cannot decode it. Multi-region setups therefore need a registry whose IDs mean the same thing everywhere, either through a primary registry in one region with read-only followers elsewhere, or through a feature that links registries and keeps IDs aligned. Schema changes must be made in one place and be compatible in both directions.

Kafka Connect and source systems

Connectors that pull from databases or push to storage must also be planned. If both regions run the same connector against the same database, the same rows are published twice. Decide which region owns each source, run the connector only there, and let replication share the results. Sinks are similar: a sink that writes to a database in each region must write idempotently, since it will see duplicates.

Access rules and settings

Access control lists, called ACLs, say who may read or write which topic. Topic settings, such as retention and compaction, say how long data lives. MM2 can copy both, but you should review what it copies, since the mirrored topic name changes and some rules must be rewritten for it. Also decide whether topic settings and partition counts should match across regions. Matching partition counts is important because it preserves key-to-partition mapping.

Data shape

Schemas

IDs must decode everywhere. Change them in one place. Keep changes backward and forward compatible.

Sources

Connectors

One owner region per source system. Avoid running the same source connector in two regions.

Sinks

Idempotent writes

Sinks must tolerate repeated records, since copying between regions can deliver the same record twice.

Access

ACLs

Grant applications rights on the local topic and read rights on mirrored ones. The replicator needs its own narrow identity.

Settings

Topic configs

Retention and compaction should suit each region’s needs. Keep partition counts equal for mirrored topics.

Control

Metadata quorum

Each cluster runs its own. Never stretch one quorum across two far regions unless you have three.

i
Find the hidden single point of failure

List everything the application needs to run: registry, identity provider, secrets store, DNS, feature flags, databases. If any one of them lives in a single region, your active-active Kafka will still stop when that region stops.

19Failure Scenarios: Region Outage, Link Cut and Split Brain

A design is only as good as its behaviour on the worst day. Walk through the main failures before they happen.

flowchart TD
  X["Alert: replication lag rising or region unreachable"] --> Q1{"Is the region itself
healthy for its own users?"} Q1 -- "yes" --> Q2{"Is the link between regions broken?"} Q2 -- "yes" --> LC["Link cut: both regions keep working
backlog grows on the copier"] Q2 -- "no" --> SL["Copier too slow or under-sized
scale workers, check throttles"] Q1 -- "no" --> Q3{"Confirmed by more than one
independent signal?"} Q3 -- "yes" --> RO["Region outage: shift traffic
to the surviving region"] Q3 -- "no" --> MON["Monitoring blind spot: verify before acting"] LC --> HEAL["Link returns: backlog drains, conflicts resolved by rules"] RO --> FB["Later: recover the region and fail back"]
Figure 10. A first triage tree. Most alarms are slow copying or a broken link, not a dead region. Confirm before shifting traffic.
FailureWhat happensWhat you do
One broker failsKafka elects new leaders inside the clusterNothing in multi-region terms; normal repair
A zone failsReplicas in other zones take overConfirm partitions are in sync; nothing to fail over
Replicator crashesCopying pauses, lag grows, then it resumes and may repeat some recordsRestart, watch lag, confirm idempotent readers
Link between regions cutBoth regions keep serving locally; remote data goes staleKeep serving; expect a burst of data and possible conflicts on return
Region completely lostData not yet copied is lost; the other region is the only truthShift traffic, take over ownership of the lost region’s entities, accept the gap
Bad deployment in one regionWrong data may be written locally and replicated onwardStop the replicator quickly, isolate, repair, replay
Poison or corrupt dataThe bad record is faithfully copied everywhereQuarantine, add filters, use dead-letter topics

Split brain

A split brain happens when two regions each believe they alone are in charge and both act as the owner of the same data. It usually follows a network split, when each region thinks the other is dead. Home-region design plus a single authority for ownership changes prevents most of these. If the ownership rules are decided by a small strongly consistent service, only the side that can reach a majority of that service is allowed to take over.

!
Replication copies mistakes too

Replication is not a backup. A bad deployment that writes wrong records, or an accidental topic deletion, is copied quickly to every region. Keep separate protection, such as longer retention, archived copies and the ability to stop the replicator in seconds.

20Failover and Failback Playbook

A written, rehearsed sequence of steps beats improvisation at three in the morning.

Failover means moving work from a failed region to a healthy one. Failback means moving it back after repair. In active-active, the surviving region is already running, so failover is mostly about redirecting users and taking over ownership. The steps below assume a home-region design with translated offsets.

1

Declare and confirm

Use more than one independent signal, such as health checks from outside and the heartbeat topic, and name one person who is allowed to make the call.

2

Redirect user traffic

Change DNS or the global load balancer so users reach the healthy region. Lower record lifetimes in advance, or the change takes hours to spread.

3

Take over ownership

Update the ownership table so the healthy region becomes home for the lost region’s entities. Do this once, through the single authority.

4

Move consumers

Start the applications that used to read from the lost cluster in the healthy region, using translated offsets, and confirm they resume near the right place.

5

Watch duplicates and gaps

Expect a burst of repeats. Check business numbers such as order counts against another source, and record the gap in data for later reconciliation.

6

Recover the failed region

When it returns, do not send it traffic yet. It still holds old data and possibly stale ownership. Bring the cluster up, let replication catch up in both directions, and compare.

7

Fail back deliberately

Move ownership and users back in small steps, with a hold period between steps. Failback is a second, planned migration and deserves the same care as any other.

Production example: the region that came back too eagerly

A team lost a region for two hours. When it returned, automation immediately put it back into service. It still believed it owned certain customers, while the surviving region had taken them over. For a few minutes both regions accepted changes for the same customers, and reconciliation took days. The fix was a rule that a returning region starts in read-only mode and needs a human to approve ownership return. The lesson: recovery is a risky moment, and it must be slower than failure.

21Performance, Scalability and Cost

Cross-region traffic is the slowest and most expensive part of the system. Most tuning is about moving fewer bytes, more efficiently.

Where the time goes

A replicated record pays for a fetch from the source, a trip over a long link, and a write on the target. The long link has high latency, so the copier must keep many bytes in flight at once. TCP connections with small buffers can leave a fast link mostly idle. Larger socket buffers, bigger batches and compression usually raise throughput far more than adding more copier tasks.

Bytes

Compress

Compressing batches before they cross the link cuts transfer volume, and often cost, by half or more for text-like data.

Batches

Batch larger

Larger batches and a small linger delay let each request carry more, which matters more on a long link.

Network

Buffers

Raise socket buffer sizes so the link stays full. A long, fast link needs more data in flight than a short one.

Parallel

Tasks and workers

Add tasks up to the number of source partitions. More tasks than partitions do nothing.

Filter

Copy less

Replicate only the topics other regions need. Internal, temporary and region-private topics should stay home.

Read

Follower fetching

Let local consumers read from a nearby replica, so read traffic does not cross zones or regions needlessly.

Cost is a design input

Cloud providers charge for data leaving a region. A design that replicates every topic in both directions to three regions multiplies transfer cost. Count it early: bytes per day per topic, times the number of destination regions, times the provider’s rate. Many teams are surprised to find that transfer costs exceed the cost of the brokers themselves.

Capacity planning for the survivor

When one region dies, the other must carry its traffic. If each region runs at 70 percent of capacity, the survivor needs 140 percent, and it will fail. Size each region to handle the whole load, or accept planned degradation, such as turning off non-critical features during an outage. The same holds for the replicator: after an outage, the backlog must drain at the same time as normal traffic, so it needs spare throughput.

2x
rule of thumb: plan each region to carry the whole load, or plan what to switch off
50%+
typical volume saving from compression on text-like records (illustrative)
= partitions
upper limit on useful copier tasks for one topic

22Growing the Design: Adding a Region and Migrating Safely

Systems grow. Adding a third region is easier when you plan the steps and keep every change reversible.

Adding a region is not just building another cluster. Every existing region must start copying to it, it must start copying to every existing region, and the ownership table must learn about it. With two regions there are two replication directions. With three there are six. With four there are twelve. The count grows with the square of the regions, and so do the number of dashboards, alerts and ways to make a mistake.

1

Build and test the empty region

Create the cluster, security, monitoring and schema access. Send test events to prove local operation before any data flows.

2

Copy in one direction first

Start replicating from existing regions into the new one. Let it catch up and check that lag is steady. Nothing depends on it yet.

3

Start read-only applications

Run readers in the new region against mirrored data. This proves schemas, permissions and offsets under real data.

4

Copy back out

Only now start replication from the new region to the others, with its local topics still empty or only test data.

5

Move a small slice of users

Assign a few home entities to the new region. Watch conflict counters, lag and error rates. Increase gradually.

Watch out for the quadratic growth

Some teams avoid the full mesh with a hub-and-spoke layout, where every region copies to and from a central hub cluster, not directly to every other region. This cuts links from the square to a straight line, but it adds an extra hop for data crossing between two spokes, and the hub becomes a critical component that must itself be highly available. Choose the mesh for small numbers of regions and low delay, and hub-and-spoke when the region count grows.

LayoutLinks for N regionsDelay between two regionsMain risk
Full meshN times (N minus 1)One hopMany links to run, secure and watch
Hub and spoke2 times NTwo hopsThe hub is a shared dependency
!
Keep every step reversible

At each stage, know exactly how to undo it: stop the replicator, move users back, drop the mirrored topics. A migration you cannot reverse is a bet, not a plan.

23Configuration Reference

The settings that matter most, grouped by where they live. Names follow open-source Apache Kafka; check them against your version.

MirrorMaker 2 settings

SettingWhat it controlsGuidance
clusters and cluster aliasesNames for each cluster, used as topic prefixesShort, permanent, region-based
Source-to-target enabled flagsWhich direction copies runEnable both directions for active-active, each with its own topic filters
topics and topics.excludeWhich topics are copiedUse explicit allow lists; exclude internal and already-mirrored topics
groupsWhich consumer groups get translated offsetsInclude the groups that may fail over
replication.policy.classNaming rule for remote topicsKeep the default prefixing policy for two-way copying
replication.factorCopies of each mirrored topic on the targetSame as local production topics, usually three
sync.topic.configs.enabled and sync.topic.acls.enabledCopy topic settings and access rulesReview what is copied; rewrite rules for prefixed names
emit.checkpoints.enabled and emit.heartbeats.enabledWrite checkpoints and heartbeatsKeep both on
sync.group.offsets.enabledApply translated offsets to target groups automaticallyUse where only one side is active per group
refresh.topics.interval.secondsHow often new topics are discoveredLower it if new topics must appear fast
tasks.maxParallel copy tasksUp to the partition count of the busiest topics

Producer, consumer and broker settings that matter

WhereSettingWhy it matters
Local producersacks=all, idempotence onKeeps local writes safe before replication even starts
Broker and topicmin.insync.replicas of two with replication factor threeA local write is durable in the region before the far copy begins
Brokerunclean leader election offPrevents silent local data loss that would then be replicated onward
Copier producercompression, batch size, lingerEfficient use of the long link
Copier consumerfetch size, receive bufferKeeps the link full despite delay
Brokerrack labels and follower fetchingCuts cross-zone read traffic
Local consumersread committed for transactional topicsAvoids reading aborted data from local topics
i
Keep configuration as reviewed files

Store the replicator configuration in version control and review every change like code. A single mistaken topic filter can start a loop or stop replication silently.

24Security and Compliance

Data crossing regions crosses trust boundaries, legal boundaries and public networks. Each one needs a lock.

Protect the link

All cross-region traffic must be encrypted in transit with TLS, and both ends must prove who they are. Mutual TLS or a strong authentication mechanism, such as SASL with short-lived credentials, prevents an attacker from posing as a replicator. Prefer private connectivity, such as peering or dedicated links between regions, over the public internet.

Give the replicator the smallest rights

The replicator needs to read the topics it copies and write to the mirrored topics, and to manage its own internal topics and groups. It should not have administrator rights over the whole cluster. Separate identities per direction help: the identity that writes into Region B should not be able to write to local topics that only local applications own.

Respect data boundaries

If some data must stay in a country, do not replicate its topic, or replicate only a filtered, masked version. A common design keeps personal topics regional and publishes derived, anonymous events for global use. Test the filter, because a missing filter is a legal problem, not a bug.

Other important points

  • Encrypt data at rest in every region with keys managed in that region, or with keys that each region can reach even when another region is down.
  • Audit who reads mirrored topics; the copy in Region B is as sensitive as the original in Region A.
  • Rotate credentials in both regions on a schedule, and rehearse it. A certificate that expires in one region can silently stop replication.
  • Apply retention rules consistently. Deleting a customer’s data means deleting it in every region, including mirrors and backups.
!
Deletion is harder than copying

Laws that require deleting a person’s data apply to every copy. Design a way to trace and remove data from mirrored topics, such as compacted topics with tombstone records, or per-customer encryption keys that can be destroyed.

25Monitoring, Logging and Metrics

You cannot manage what you cannot see. A few well-chosen signals tell you whether both regions are healthy and in step.

flowchart LR
  subgraph RA["Region A"]
    KA["Kafka A metrics"]
    MMA["Replicator metrics"]
  end
  subgraph RB["Region B"]
    KB["Kafka B metrics"]
    MMB["Replicator metrics"]
  end
  HB["Heartbeat topic
end-to-end delay"] KA --> DASH["Dashboards and alerts
hosted outside both regions"] KB --> DASH MMA --> DASH MMB --> DASH HB --> DASH DASH --> ONC["On-call engineer"]
Figure 11. Watch from outside. If the dashboards live in the region that fails, you lose your eyes exactly when you need them.
SignalWhat it tells youTypical alert
Replication latency and record ageHow late the far copy is, in timeAbove your RPO target for several minutes
Replication throughputBytes copied per secondDrops to zero while producers are active
Heartbeat delayEnd-to-end health even when traffic is quietHeartbeat older than a few intervals
Checkpoint latencyFreshness of translated consumer offsetsMuch older than the expected interval
Copier task failures and restartsStability of the pipeRepeated restarts
Consumer lag per region and per prefixed topicWhether readers keep up with mirrored dataGrowing lag on any aggregate reader
Under-replicated partitions in each clusterLocal cluster healthAny sustained value above zero
Cross-region bytes and costMoney leaving the doorSudden jump versus a baseline
Duplicate and conflict counters in applicationsHow often repeats and conflicts really occurSharp increase after a release or failover

Measure RPO directly

Replication lag expressed in records is hard to interpret. Convert it into time: “the far region is 4 seconds behind”. That number is your live estimate of how much would be lost if the source region vanished right now. Track it, graph it, and alert when it exceeds the business target.

Logging that helps later

Log every ownership change, every failover step, and every conflict decision with the event ID and region. When a customer asks why their update disappeared, this log is the only way to answer.

i
A synthetic canary

Send a tiny test event from each region every few seconds and check that it appears in every other region. It proves the whole path, including permissions, naming and schemas, works right now.

26Design Patterns and Anti-Patterns

A few shapes recur in every successful design, and a few mistakes recur in every painful one.

Patterns worth using

Pattern

Local write, remote read

Every application writes only to its local cluster, and other regions receive data by replication. Keeps writes fast and blame clear.

Pattern

Home region per entity

One owner region per customer or account. Forward writes there. Conflicts disappear by design.

Pattern

Idempotent consumer

Every event has an ID. Consumers remember or overwrite by ID so repeats do nothing.

Pattern

Outbox

A service writes its database change and its event in one local step, and a relay publishes the event to Kafka. No lost or phantom events at the edge.

Pattern

Aggregate cluster

A separate cluster gathers data from all regions for analytics, isolating heavy readers.

Pattern

Regional kill switch

A control that can stop replication or reroute traffic in seconds when something bad is spreading.

Anti-patterns to avoid

ANTI-PATTERN 01Avoid
What people do

They write to the same topic name in both regions and copy in both directions without prefixes.

Why it hurts

Records loop between the clusters, or two writers interleave in a topic that nobody can reason about. Ordering and ownership are lost.

Do this instead

Use one writer region per topic, prefixed remote topics, and readers that combine them.

ANTI-PATTERN 02Avoid
What people do

They stretch a single cluster across continents to get zero data loss.

Why it hurts

Every write waits for a trip across an ocean, throughput falls, and a network split can stall everyone.

Do this instead

Use separate clusters and accept a small, measured RPO, or keep the stretch cluster inside one metropolitan area.

ANTI-PATTERN 03Avoid
What people do

They assume that a consumer’s saved offset in Region A can be used unchanged in Region B.

Why it hurts

The consumer skips data or repeats a huge range, and nobody notices until numbers are wrong.

Do this instead

Use translated offsets, keep consumers idempotent, and rehearse failover.

ANTI-PATTERN 04Avoid
What people do

They use “last writer wins” on wall-clock times for important data, such as balances or inventory.

Why it hurts

Clock differences silently discard real updates, and the loss is invisible.

Do this instead

Give the data a single owner, or use version numbers or mergeable data types.

ANTI-PATTERN 05Avoid
What people do

They size each region for its own traffic only.

Why it hurts

When one region fails, the survivor is overloaded and fails too, turning a regional outage into a global one.

Do this instead

Plan for the survivor to carry the load, or define exactly what gets switched off.

27Real-World and Industry Examples

How well-known companies have described their multi-region streaming setups in public, in general terms.

i
A note on these examples

These summaries are based on publicly shared talks and articles and are described in general terms. Details change over time and I did not check them against live sources. Treat them as illustrations of patterns, not as exact descriptions of any company’s current systems.

Ride hailing

Uber

Has described running Kafka clusters in several regions with in-house replication tooling, aggregate clusters for global views, and a service that helps consumers resume in another region after failover. The pattern: regional clusters plus aggregation plus offset handling.

Social network

LinkedIn

Created Kafka and has described mirroring data between data centers into aggregate clusters, later building its own replication service to handle scale and reliability. The pattern: local production, central aggregation.

Streaming video

Netflix

Has shared that its event pipelines run in multiple regions so that traffic can be moved away from a struggling region. The pattern: regional pipelines that can absorb each other’s load.

Payments

Financial platforms

Commonly keep each account in a home region and use idempotency keys everywhere, since duplicates are unavoidable. The pattern: single owner plus idempotent processing.

Commerce

Global retailers

Often replicate catalog and price events to every region while keeping orders local to the customer’s region. The pattern: share reference data widely, keep transactions close to home.

Travel

Booking platforms

Need availability data everywhere but must avoid double booking. The pattern: single owner per inventory item, replicated views elsewhere.

What these stories have in common

None of them tries to make every region write every fact at will. They give data a natural home, replicate views of it elsewhere, treat duplicates as normal, and invest heavily in tools to observe lag and to move traffic. The technology is the smaller part of the work. The larger part is deciding who owns each piece of data.

28Best Practices and Common Mistakes

A checklist to review before going live, and a list of mistakes that show up again and again.

Do

  • Decide the RPO and RTO in numbers before choosing a design.
  • Give every entity a home region and route writes to it.
  • Add event ID, origin region and event time to every record.
  • Make every consumer and sink idempotent.
  • Keep topic names prefixed and one writer region per topic.
  • Monitor lag in seconds and alert against the RPO target.
  • Run dashboards and alerting outside the regions they watch.
  • Rehearse failover and failback on a schedule.

Do not

  • Do not assume replication is a backup.
  • Do not fail over automatically on a single signal.
  • Do not reuse local consumer offsets in another cluster.
  • Do not run the same source connector in two regions.
  • Do not size regions only for their own traffic.
  • Do not forget to plan the trip home after failover.
  • Do not change schemas in two places at once.
  • Do not depend on wall-clock timestamps for critical decisions.

Mistakes seen most often

  • Treating asynchronous as synchronous. Teams tell the business that no data can be lost, then discover the far copy was seconds behind.
  • Forgetting the small services. Kafka is multi-region, but the schema registry, identity provider or configuration store sits in one region.
  • Testing only the happy path. Failover is tried once in a calm test, never under load, never with a partial network failure.
  • Unbounded reprocessing. After failover, a consumer starts from the beginning of a topic and overloads downstream systems.
  • Silent filters. A replicator topic filter that does not match a new topic means the data is never copied and nobody notices for weeks.

29Testing and Game Days

Confidence in a multi-region design comes from breaking it on purpose while everyone is watching.

A game day is a planned exercise in which the team injects a failure into a real or production-like environment and observes what happens. The point is not to prove that the design works. The point is to find what does not, while the cost is low.

1

Baseline test

Send known events through every path and confirm they arrive, once or with harmless repeats, with correct prefixes and schemas.

2

Slow the link

Add delay and packet loss between regions. Watch lag, alerts, and whether applications behave sensibly with stale remote data.

3

Cut the link

Block traffic entirely for a while, then restore it. Check the backlog drain and any conflicts that appear.

4

Kill the replicator

Stop and restart it mid-stream. Count duplicates and confirm that consumers absorb them.

5

Lose a region

Shut a region down under load. Time the traffic shift, offset translation and ownership takeover against your RTO.

6

Fail back

Restore the region and return traffic in stages. This step is skipped surprisingly often, and it is where surprises hide.

i
Write down what you learn

After each exercise, turn every surprise into a fix, an alert or a runbook step. A game day with no written findings is just a stressful afternoon.

30Worked Example: An Imaginary Shop Called CartCloud

All the ideas together in one made-up system. The company and numbers are invented for teaching.

CartCloud sells goods to customers in India, Southeast Asia and Europe. It runs three regions, which we call Mumbai, Singapore and Frankfurt. Its goals are simple: pages must feel fast everywhere, the shop must survive the loss of any one region, and customer data of European customers must stay in Europe.

flowchart TB
  subgraph MUM["Mumbai"]
    AM["Cart and order services"] --> KM["Kafka Mumbai"]
  end
  subgraph SIN["Singapore"]
    AS["Cart and order services"] --> KS["Kafka Singapore"]
  end
  subgraph FRA["Frankfurt"]
    AF["Cart and order services"] --> KF["Kafka Frankfurt"]
  end
  KM -- "catalog and price events" --> KS
  KS -- "catalog and price events" --> KM
  KM -- "catalog and price events" --> KF
  KF -- "catalog and price events" --> KM
  KS -- "catalog and price events" --> KF
  KF -- "catalog and price events" --> KS
  KM -- "anonymous order stats only" --> AGG["Global analytics cluster"]
  KS -- "anonymous order stats only" --> AGG
  KF -- "anonymous order stats only" --> AGG
Figure 12. CartCloud. Catalog and price events go to every region. Orders stay in their home region. Only anonymous statistics reach the analytics cluster.

Decisions and reasons

DecisionReason
Three separate clusters, no stretch clusterRegions are far apart. Local writes must stay fast.
Each customer has a home region, chosen by where they signed upOrders and carts have one owner, so no conflicts. European customers are homed in Frankfurt and their personal topics are never replicated out.
Catalog and price events are written by a central team in Mumbai, and mirrored to the othersReference data with a single author needs no conflict handling. Other regions read the prefixed topic.
Consumers read local plus remote topics through name patternsEvery region builds a full catalog view.
Every event carries event ID, origin region and event timeDeduplication and debugging.
Order processing overwrites by order ID, and payment calls use idempotency keysRepeats after failover do no harm.
Replication lag is reported in seconds; alert at 30 seconds, page at 120Business accepts up to two minutes of possible loss in a total region loss.
Each region is sized to carry 60 percent of global traffic; non-critical features switch off during a region lossBalances cost against survival.

A bad day: Singapore goes dark

1

Detection

External probes and the heartbeat topic both show Singapore missing. Two independent signals agree, and the on-call lead declares an outage.

2

Traffic shift

The global load balancer sends Singapore’s users to Mumbai. Short DNS lifetimes set in advance make this take minutes.

3

Ownership takeover

Singapore’s customers are reassigned to Mumbai through the ownership service. Mumbai starts accepting their changes.

4

Consumer resume

Jobs that read Singapore’s data start in Mumbai from translated offsets. A burst of repeats appears and is absorbed by order-ID overwrites.

5

Gap reconciliation

The last 40 seconds of Singapore orders, which had not yet been copied, are missing. Payment records from the payment provider are compared, and the gap is reconstructed.

6

Recovery and failback

Singapore returns in read-only mode. Replication catches up both ways. After review, ownership and traffic move back in three stages over a day.

The plan works because most decisions were made months before the outage.

31Choosing the Right Design: A Decision Guide

Answer a few honest questions and the right layout usually becomes obvious.

flowchart TD
  S["Start: do you need more than one region?"] --> Q1{"Must the service survive
a whole region loss?"} Q1 -- "no" --> SR["Single region across 3 zones"] Q1 -- "yes" --> Q2{"Is zero data loss required
and are sites close together?"} Q2 -- "yes" --> ST["Stretch cluster over 3 nearby sites"] Q2 -- "no" --> Q3{"Do users in several regions
need fast local writes?"} Q3 -- "no" --> AP["Active-passive with replication
and rehearsed failover"] Q3 -- "yes" --> Q4{"Can each entity have
one home region?"} Q4 -- "yes" --> AA1["Active-active with home regions
simplest and safest"] Q4 -- "no" --> AA2["Active-active with merge rules
highest effort, use with great care"]
Figure 13. A decision tree. Most teams end at active-passive or at active-active with home regions.

Choose active-active when

  • Users in several regions need fast local writes.
  • Recovery time must be seconds or minutes, not an hour.
  • Data has a natural owner region, or changes can be merged safely.
  • The team can run and rehearse a complex system.

Prefer something simpler when

  • One region takes all writes and a short outage is acceptable.
  • No clear ownership exists and merges are dangerous.
  • The team is small and has no failover practice.
  • Cross-region cost would outweigh the business value.
i
Start passive, grow active

A safe path is to begin with active-passive, prove replication and failover, then make the second region active for selected topics one by one. Each step teaches the team something before the stakes rise.

32Frequently Asked Questions

Short answers to questions that come up in almost every design discussion.

Q1Can I get zero data loss across far-apart regions?
Only with synchronous replication, such as a stretch cluster, and you then pay the region round trip on every write. With separate clusters and asynchronous copying, expect a small, measurable loss window.
Q2Is active-active always better than active-passive?
No. It recovers faster and uses both regions, but it adds conflicts, ownership rules and operational work. Many systems are better served by a well-tested active-passive setup.
Q3Why do topic names get a prefix?
The prefix says where a topic’s data came from, and it stops replicators from copying a topic back to the place it came from, which would create a loop.
Q4Does MirrorMaker 2 give exactly-once copying?
Treat it as at-least-once. Duplicates can appear after restarts, so downstream logic should be idempotent. Some newer connector features may narrow the gap, but check your version and test.
Q5Are consumer offsets the same in both regions?
No. With record-by-record mirroring, offsets differ. Use translated offsets from the checkpoint mechanism. Some vendor linking features keep offsets identical, but their mirror topics are read-only.
Q6What if a message is edited in two regions at once?
That is a conflict. Avoid it by giving each entity one home region. If you cannot, use version numbers, mergeable data types or a clearly defined rule, and record every decision.
Q7Where should MirrorMaker 2 run?
Usually close to the target cluster, so that it reads across the long link and writes locally. Some teams choose otherwise for specific network or security reasons.
Q8How many regions should I use?
As few as meet your goals. Two regions give survival. Three give better spread and safe quorum choices. Each extra region multiplies copying cost and complexity.
Q9Do transactions work across regions?
Transactions are atomic inside one cluster. When records are mirrored, they are copied as separate records, and the atomic grouping is not carried over as a unit. Design downstream logic accordingly.
Q10Should internal topics of Kafka Streams be replicated?
Generally no. Run the application in each region with its own internal topics, and make its logic order-insensitive or single-owner.
Q11How do I know how much data I would lose?
Measure replication lag in seconds. At any moment, that value approximates the data at risk if the source region were lost immediately.
Q12Can I replicate only part of a topic?
Not with plain mirroring, which works at topic level. To copy a subset, write a small filtering application, or split data into separate topics by sensitivity or destination.
Q13What happens to compacted topics?
Mirrors are compacted on their own schedule, and tombstones are copied. The final state converges, but the exact records kept may differ between regions.
Q14Is replication a backup?
No. It copies mistakes and deletions quickly. Keep separate protection such as long retention or archives.
Q15How often should we rehearse failover?
At least a few times a year, after every major change, and always before a peak season. Rehearse failback too.
Q16What is the biggest hidden cost?
Cross-region data transfer and the people time needed to operate and test the system. Estimate both before you commit.
Q17Can consumers read from another region’s cluster directly instead of replicating?
Yes, but every read then pays the long delay and depends on that region being up. It is fine for rare cases and poor for main paths.
Q18Do I need a schema registry in every region?
You need schema lookups to work in every region, even during a regional outage. That means either a local registry with aligned IDs or a highly available one that all regions can reach.
Q19Should each region have its own consumer group names?
Often yes. A group name that includes the region makes it obvious where an application runs, and stops automatic offset syncing from colliding with a group that is already active in the target cluster. For jobs that may fail over, choose names deliberately and document the rule.
Q20How long can a region be disconnected before recovery becomes hard?
It depends on topic retention and the copier’s speed. If the link is down for longer than the source topic retains data, the missing records are gone before they can be copied. Set retention comfortably longer than the longest outage you plan to survive, and check that the copier can drain a backlog faster than new data arrives.
Q21Is it safe to change partition counts on mirrored topics?
Treat it as a planned change. Adding partitions changes which partition new records with a given key land in, on the source and, after refresh, on the mirror. Readers that rely on per-key order across the change need a careful cut-over, so schedule it with the consumers in mind.
Q22What is the simplest useful first step?
Set up one-way replication of a single low-risk topic to a second region, add lag monitoring and a heartbeat, and rehearse a consumer move using translated offsets. You will learn most of the important lessons at very low risk.

33Summary and Key Takeaways

The whole guide in a few sentences.

Key Takeaways

  • Two ways to span regions. A stretch cluster gives zero loss but pays the round trip on every write and needs three close sites. Separate clusters with asynchronous replication keep writes fast and accept a small, measurable loss window.
  • One writer per topic. Use prefixed remote topics so each topic has a single writer region, loops are impossible, and readers combine local and remote topics.
  • Avoid conflicts, do not merge them. Give every entity a home region and route writes to it. Use versions or mergeable data only where you truly cannot.
  • Offsets need translation. Move consumers with translated offsets, expect a few duplicates, and never fall back on the latest position.
  • Design for repeats. Carry event ID, origin and time in every record, and make every consumer and sink idempotent. Exactly-once does not cross the asynchronous copy.
  • Watch lag in seconds and rehearse. Alert against the RPO target, keep monitoring outside the regions, and practise failover and failback on a schedule.
  • Size and secure for the bad day. The survivor must carry the load, links must be encrypted and authenticated, and data boundaries must be enforced by filters you have tested.