Metadata Caching on Brokers in Kafka
Every produce and fetch request in Kafka begins with a question: who is in charge of this partition right now? This guide explains how brokers answer that question in microseconds from a local copy, how that copy stays fresh, and what happens when it goes stale.
Imagine walking into a huge airport with no signs. Before you can board, you must find your gate, and the only way is to phone the central control room and ask. Now imagine ten thousand passengers doing the same thing every few seconds. The control room would drown in calls, and every flight would be late.
Real airports solve this with departure boards. The control room decides which flight uses which gate and sends updates to screens in every terminal. Passengers read the nearest screen, which is fast and needs no phone call. Apache Kafka works the same way. A small group of machines decides who leads each partition, and every broker keeps its own local copy of those decisions, called the metadata cache.
This guide explains that cache from the ground up: what is stored in it, how updates travel to every broker, how clients keep their own second copy, what goes wrong when copies disagree, and how to configure and monitor it in production. Each new term is explained when it first appears, and each big idea comes with an everyday comparison and real-world context.
KKafka in Five Minutes
A short refresher on the moving parts, so that the rest of the guide has firm ground to stand on.
Apache Kafka moves streams of events between programs. A program that writes events is a producer, and a program that reads them is a consumer. The servers that store the events are brokers, and a group of brokers is a cluster. Events are organised into named streams called topics, and each topic is cut into partitions, which are ordered lists of messages that can live on different brokers so the work can be shared.
For safety, every partition is copied to several brokers. Each copy is a replica. One replica is the leader and handles all reads and writes for that partition. The others are followers that copy the leader. The set of replicas that are keeping up is the in-sync replica set, or ISR.
Picture an airline with many flights. A topic is a route, a partition is one specific flight on that route, and the leader is the gate where boarding for that flight happens. Passengers can only board at the right gate. If they walk to the wrong one, they are politely sent away.
Producer
Sends messages to a partition. It must know which broker currently leads that partition.
Consumer
Reads messages from a partition leader, and also needs to find its group coordinator.
Broker
Stores partitions and answers requests. Every broker knows about the whole cluster, not only its own data.
Topic and partition
A topic is split into partitions. Order is kept inside a partition, and partitions spread across brokers.
Replicas
Several copies of each partition on different brokers, with one leader and the rest followers.
Metadata
The map that says which topic exists, where each partition lives, and who leads it right now.
Every client action in Kafka starts with “where do I send this?” The answer lives in the metadata. Everything else in this guide is about how that answer is stored, spread, kept fresh and used safely.
WWhat Is Kafka Metadata?
Metadata means “data about data”. In Kafka it is the description of the cluster itself, not the messages inside it.
Your messages are the cargo. The metadata is the map of the airport: which gates exist, which flight uses which gate, and which staff are on duty. It is small compared with the cargo, but without it nothing can be delivered. Kafka keeps several kinds of metadata, and each kind answers a different question.
| Kind of metadata | Question it answers | Example |
|---|---|---|
| Broker list | Which brokers are alive and at what addresses? | Broker 1 at host-a:9092, Broker 2 at host-b:9092 |
| Topics and partitions | What topics exist and how many partitions has each? | Topic orders has 12 partitions |
| Leaders and epochs | Who leads each partition, and under which leader epoch? | Partition 3 of orders: leader Broker 2, epoch 7 |
| Replicas and ISR | Where are the copies and which are in sync? | Replicas on brokers 1, 2, 3; ISR is 2 and 3 |
| Configuration | Which settings apply to a topic or broker? | Retention of seven days on orders |
| Access rules | Who may read, write or administer what? | Service payments may write to orders |
| Quotas | How much traffic may a client use? | Client reporting is limited to a set bandwidth |
| Feature levels | Which cluster-wide features are turned on? | The metadata version the cluster runs at |
Why every broker needs the whole picture
A broker stores only some partitions, yet it must know about all of them. A client may connect to any broker and ask about any topic, so each broker must be able to answer for the whole cluster. A broker also has to check, on every produce or fetch request, whether it is the correct leader for the partition being asked about, and it must reject requests that arrive at the wrong place. These checks happen thousands of times a second, and they all read the metadata.
A topic is created once but used millions of times. A leader changes only during failures or maintenance but is checked on every request. This pattern, rare writes and enormous reads, is the perfect situation for a cache.
PThe Problem a Cache Solves
To see why brokers keep a local copy, imagine the design without one.
Suppose the truth about the cluster lived in one place, and every broker had to ask that place a question each time a request arrived. A single busy broker may handle tens of thousands of requests each second. Multiplied across a cluster with dozens of brokers, that is a flood of lookups aimed at one small service. The lookups would add delay to every request, and the service would become both a bottleneck and a single point of failure.
It is the phone-the-control-room airport again. The control room knows everything, but it can only answer so many calls. Departure boards fix this by copying the answers to where people need them. The control room now speaks only when something changes, and reads happen locally at the speed of looking at a screen.
What a cache buys
Lookups in memory
Reading a value from the broker’s own memory takes far less time than a network call. Checking the leader of a partition becomes nearly free.
Load spread across all brokers
Any broker can answer a metadata question, so client questions are spread over the whole cluster, not aimed at one machine.
Work continues during trouble
If the controller is briefly busy or changing over, brokers still serve requests from what they already know.
Any broker as an entry point
A client needs only one working address to find out about the entire cluster. That address can be any broker.
What a cache costs
A copy can become out of date. If the control room changes a gate and one screen has not updated yet, a passenger may walk to the wrong place. Kafka accepts this, because a wrong answer is cheap to correct: the request is rejected with a clear error, the client asks again, and the next answer is right. The rest of this guide is about making those corrections quick, safe and rare.
Pros of a local cache
- Very fast reads from memory
- Load is spread across every broker
- Clients need only one bootstrap address
- Brokers keep working through short controller hiccups
Cons of a local cache
- Copies can briefly disagree
- Stale answers cause errors and retries
- Updates must reach every broker reliably
- Memory grows with the number of partitions
BThe Broker Metadata Cache at a Glance
Here is the whole system in one picture, followed by a walk through each part.
flowchart LR CQ["Controller quorum
holds the metadata log"] -->|"metadata records"| B1["Broker 1
local metadata cache"] CQ -->|"metadata records"| B2["Broker 2
local metadata cache"] CQ -->|"metadata records"| B3["Broker 3
local metadata cache"] P["Producer"] -->|"Metadata request"| B1 C["Consumer"] -->|"Metadata request"| B2 A["Admin tool"] -->|"Metadata request"| B3
Read the figure from left to right. On the left is the controller quorum, a small group of servers that makes decisions about the cluster and records each decision in an ordered log. In the middle are the brokers. Each one reads that log and builds its own in-memory picture of the cluster, which is the metadata cache. On the right are the clients. They send Metadata requests to whichever broker they can reach and receive answers straight from that broker’s cache.
Two layers of caching
There are really two caches involved, and it helps to keep them apart. The first lives inside every broker, and it is a copy of the cluster’s state. The second lives inside every client, and it is a copy of the parts of that state the client cares about. A client asks a broker, keeps the answer for a while, and reuses it. The airport version: the broker cache is the departure board, and the client cache is the gate number printed on the passenger’s boarding pass.
| Cache | Where it lives | Who fills it | How it is kept fresh |
|---|---|---|---|
| Cluster state cache | Inside each broker’s memory | The controller, through the metadata log | Brokers apply new records continuously |
| Client metadata cache | Inside each producer, consumer or admin client | Any broker, through Metadata requests | Periodic refresh and refresh on errors |
Kafka also relies heavily on the operating system’s page cache, which holds recent message data in memory for fast reads and writes. That is a different thing. In this guide “metadata cache” always means the cluster map, not the message data.
CThe Controller: Source of Truth
Caches are copies, and copies need an original. In Kafka, the original is managed by the controller.
The controller is the part of the cluster that makes decisions about the cluster itself. It reacts when a broker joins or fails, chooses a new leader when one is lost, creates and deletes topics, changes partition counts, and updates configuration. Brokers do not make these decisions on their own, because two brokers deciding differently would leave the cluster in conflict. One authority avoids that.
The word controller has meant two slightly different things over the years. In older clusters, one ordinary broker was elected controller and used an external coordination service called ZooKeeper to store cluster state. In modern clusters, a dedicated group of controller nodes runs a built-in consensus protocol called KRaft and keeps the state in Kafka’s own log. Only one controller is the active one at a time. The others are ready to take over.
The controller is the airport’s operations centre. It assigns gates, reacts when a plane is cancelled, and announces changes. There is only one operations centre in charge at a time, with a backup team ready, so announcements never contradict each other.
What the controller must guarantee
One order of events
Every change is placed in a single, ordered sequence. Every broker applies the same changes in the same order, so all caches converge to the same picture.
Decisions survive crashes
A decision is safely stored before anyone is told about it, so a controller failure cannot erase or reverse it.
No two active controllers
A numbered term or epoch identifies the current controller, so a stale one that wakes up late is ignored.
Producers and consumers do not talk to the controller when sending or reading messages. The controller only makes and records decisions. This is exactly why brokers cache its decisions: so that the busy data path never has to wait on it.
DDeployment Shapes: Who Runs the Controller?
The same software can run in different roles. How you arrange those roles decides how well the metadata system copes with load and failure.
In modern Kafka, a server process can be given one or more roles. A broker role stores partitions and serves clients. A controller role takes part in the quorum that makes and records cluster decisions. A single process can hold both roles, called combined mode, or each can run on its own machines, called dedicated mode.
| Shape | How it looks | Best suited for | Main risk |
|---|---|---|---|
| Combined | Each node is both broker and controller | Development, tests, small clusters | Heavy data traffic can starve the controller, and a broker problem also hurts the quorum |
| Dedicated controllers | Three or five small nodes run only the controller role, separate from brokers | Production, especially larger clusters | Extra machines to run and secure |
In a small regional airfield, one person may both direct planes and load luggage. In a big international airport, the control tower is a separate building with its own staff, so a luggage jam can never delay a takeoff decision.
How many controllers?
The controller quorum needs a majority of its members to agree before any decision is committed. That is why quorums use an odd number of nodes. With three controllers, a majority is two, so the quorum tolerates the loss of one node. With five controllers, a majority is three, so it tolerates the loss of two. More than five brings little benefit, since each additional member adds more communication to every decision.
tolerates 1 failure
tolerates 2 failures
What brokers need from the controllers
Brokers are not voters. They do not count toward the majority. They only need to reach the active controller to fetch the metadata log, to register, and to send heartbeats. Each broker is told where the controllers are through configuration, and it keeps its own copy of the log on local disk. Controllers should sit on their own listener, reachable only by brokers and administrators.
Controller nodes store only the metadata log, which is small compared with message data. They need reliable disks and network, low latency between each other, and protection from noisy neighbours far more than they need large storage or many processors.
If all three controllers sit in the same rack or zone, one power event removes the majority. Place them in different racks or zones so a single incident cannot stop cluster decisions.
OThe Older Way: ZooKeeper and Pushed Updates
Understanding the old design makes the new one easier to appreciate. It also explains many older articles you may still read.
For most of Kafka’s history, cluster state was stored in an external system called ZooKeeper. The elected controller broker read that state, made decisions, wrote results back to ZooKeeper, and then pushed updates to every other broker over ordinary requests. Two request types were central. A LeaderAndIsr request told specific brokers that they had become leader or follower of certain partitions. An UpdateMetadata request told every broker the new facts about leaders, replicas and live brokers, and each broker stored them in its metadata cache.
A broker fails
Its session with ZooKeeper expires. The controller broker sees that the broker has disappeared.
The controller chooses new leaders
For each partition that the failed broker led, it picks a new leader from the in-sync replicas and writes that choice to ZooKeeper.
It tells the affected brokers
LeaderAndIsr requests go to the brokers holding the replicas so they can switch roles.
It tells everyone
An UpdateMetadata request goes to every broker so that all metadata caches learn the new leaders.
Why this design hit limits
What worked
- Simple to reason about: one controller pushing to all
- Battle-tested for years across many companies
- Brokers held a complete cache for fast local answers
What hurt at scale
- Two systems to run, secure and monitor
- A new controller had to reload all state from ZooKeeper, which grew slow with many partitions
- Pushed updates could reach brokers at different times or be missed
- Partition counts were limited in practice
When a controller failed, the replacement had to read the state of every topic and partition before it could work. With very large clusters this could take a long time, during which no leader changes were possible. This was a major reason the Kafka community designed a new approach, described in the next chapter. Recent Kafka releases, starting with version 4.0, no longer support ZooKeeper at all.
If a blog post mentions UpdateMetadata requests, controller broker election through ZooKeeper, or ZooKeeper paths, it describes the older mode. The concepts of leaders, epochs and client caching still apply. What changed is how the broker cache gets its updates.
NThe Modern Way: The Metadata Log in KRaft
In modern Kafka, the cluster’s state is itself a Kafka-style log, and brokers keep their cache up to date by reading it, much as a consumer reads a topic.
In KRaft mode, all cluster decisions are stored as records in a special single-partition log, often called the metadata log (its internal topic name is __cluster_metadata). The active controller appends each decision to this log, and the controller quorum replicates it using a Raft-style consensus protocol so that a majority of controllers hold every record before it counts as committed. Brokers are not part of the voting group, but they read the log as followers, using the same fetch mechanism that replicas use.
Instead of the operations centre phoning each terminal, it now writes every announcement into a numbered notebook that all terminals can read. Each terminal keeps reading forward from where it stopped and updates its own screen. If a terminal is switched off for an hour, it simply catches up on the pages it missed.
What the records look like
Each record in the log describes one small change. Examples include a broker registering itself, a topic being created, a partition being added, a leader or ISR changing, a configuration value being set, an access rule being added, and a feature level being raised. Since each record is a tiny, self-contained fact, a broker can apply them one by one and always know exactly which version of the cluster picture it has reached, because every record has a position number, its offset.
Broker registrations
Brokers announce themselves and their listener addresses, and the controller records which are alive or fenced.
Topic and partition records
Creation, deletion and changes to partitions, replicas, leaders and the ISR are all recorded as small events.
Configuration records
Topic and broker level dynamic settings are stored in the same log, so they reach every broker in order.
ACLs and credentials
Access rules and certain credentials are also stored, so every broker can check permissions from its own copy.
Images and deltas: how the cache is built
A broker turns the stream of records into its cache in two steps. First, it keeps an immutable snapshot of the full cluster picture, known as the metadata image. Immutable means it is never edited in place. Second, when a batch of new records arrives, the broker builds a delta that describes what changed, applies it to produce a brand-new image, and then swaps the new image in for the old one. Threads that were reading the old image keep working with it safely until they finish. Because unchanged parts are shared between old and new images rather than copied, this is cheap even for very large clusters.
Request handler threads read the cache constantly. If the cache were edited in place, readers would need locks and could see half-finished changes. With swapped immutable images, a reader always sees one consistent snapshot, and the update never blocks anyone.
MMoving From ZooKeeper: What Happens to the Caches
Many organisations ran ZooKeeper-based clusters for years. Moving them to the log-based design changes how the broker caches are filled, though not what clients see.
Because modern releases no longer support ZooKeeper, a cluster that began life in the older mode must be migrated before it can move to the newest versions. Kafka provides a migration path that lets operators do this in stages, without taking the cluster offline. Understanding what each stage does to the metadata caches makes the process less mysterious.
Add the new controllers
A quorum of controller nodes is started alongside the existing cluster. It is told to expect a migration and to read the existing state from ZooKeeper.
Copy the state into the metadata log
The new controllers read topics, partitions, configs and access rules from ZooKeeper and write them as records into their own log. The metadata picture now exists in both places.
Brokers are restarted in the new mode, one by one
Each broker is restarted with settings that make it read the metadata log instead of waiting for pushed updates. On restart it loads the log, builds its image and registers with the new controllers.
Cut over to the new controllers
Once all brokers are ready, the new controllers become the only authority, and ZooKeeper is no longer used for decisions.
Remove ZooKeeper
After a period of confidence, the old coordination service is shut down and its settings deleted.
What clients notice
Very little. Clients use the same Metadata request before, during and after the move. Rolling restarts of brokers cause the usual short waves of leader changes and client refreshes, so the migration should be done at a quiet time with the same care as any rolling upgrade. The internal path by which brokers learn about changes is what differs: pushed requests before, a fetched log after.
Benefits after moving
- One system to run and secure instead of two
- Much faster controller failover
- Room for many more partitions
- Brokers that can catch up from snapshots
Things to plan for
- Extra controller nodes to provision
- A carefully ordered, staged procedure
- Updating monitoring, which used ZooKeeper-era metrics
- Checking that any tools reading ZooKeeper directly are replaced
The exact steps, supported version combinations and rollback points depend on the release you run. Treat the outline above as a map of the ideas, and use the official upgrade documentation for your version as the source of instructions.
HHow a Broker Fills and Catches Up Its Cache
A broker that starts up has an empty head. Before it can serve anyone, it must rebuild the picture of the whole cluster, and it must prove to the controller that it has done so.
When a KRaft broker starts, it does not ask the controller for a giant dump of state. It relies on two things it keeps on its own disk: a recent snapshot of the cluster picture and the tail of the metadata log after that snapshot. It loads the snapshot, replays the log records that came after it, and ends up with an image that is close to current. Then it connects to the active controller, registers itself, and continues fetching new records to close any remaining gap.
Snapshots keep the log short
The metadata log would grow forever if every record were kept. To prevent this, Kafka periodically writes a snapshot: a compact file holding the whole cluster picture as of a certain log position. Once a snapshot exists, older log records can be discarded. By default a new snapshot is taken after a sizeable amount of new records has been added, or after a long time interval, whichever comes first. A broker that has been offline so long that the records it needs are gone can simply download the latest snapshot from the controller and continue from there.
A terminal that was closed for a month does not need to read every announcement of that month. It picks up a printed summary of today’s full departure schedule, then reads only the announcements made since that summary was printed.
Fenced until ready
A broker that has not caught up must not receive client traffic, because its cache would give wrong answers. Kafka handles this with a state called fenced. A new or lagging broker registers as fenced. The controller keeps it fenced until the broker reports that it has read the log at least as far as its own registration record. Only then is the broker unfenced and eligible to lead partitions and to appear in metadata answers.
stateDiagram-v2 [*] --> Starting Starting --> Fenced : loads snapshot and registers Fenced --> Active : caught up with the metadata log Active --> Fenced : misses heartbeats Active --> ShuttingDown : controlled shutdown ShuttingDown --> [*]
Heartbeats tie the cache to liveness
An active broker sends a regular heartbeat to the controller. By default this happens every couple of seconds, and if the controller hears nothing for several heartbeats, roughly nine seconds by default, it fences the broker and moves its partition leadership elsewhere. Each heartbeat reply also tells the broker whether it is still allowed to serve. In this way, having a fresh cache and being alive are joined together: a broker that cannot keep its cache current, or cannot talk to the controller, drops out of service rather than giving stale answers.
If a broker takes a long time to apply metadata records, because its disk is slow or its CPU is starved, it can lag behind the log. It will then answer client questions with older facts and may eventually be fenced. Watching how far each broker’s applied position trails the log is one of the best health checks for the whole system.
IInside the Broker: From Log Records to Live Behaviour
A new image in memory is only useful if the parts of the broker that need it actually notice. Here is how a change spreads through the inside of a single broker.
Inside each KRaft broker, a component reads batches of records from the metadata log as they arrive. For every batch, it collects the changes into a delta and builds a new image from the previous one plus the delta. That new image is then handed to several listeners, sometimes called publishers, each responsible for one part of the broker’s behaviour. Every listener looks at the delta and acts only on what concerns it.
The replica manager
Sees that it has become leader or follower of a partition, creates or removes log folders, starts or stops fetchers, and follows the new leader when told to.
Group and transaction coordinators
Notice when the internal topics that hold consumer group and transaction state gain or lose leaders here, and load or unload that state.
Dynamic configuration handling
Applies changed broker and topic settings, for example a new retention time, without a restart.
Quota management
Picks up changed traffic limits for users and clients and applies them to live connections.
The authorizer
Receives new and removed access rules and keeps its in-memory permission tables current.
The request layer
Reads the newest image when building Metadata responses and when validating each produce or fetch request.
When an announcement comes in at the terminal, several staff members hear it at once. The gate agent updates the gate sign, security adjusts the queue, the baggage team redirects trolleys, and the display board changes. Each acts on the part that concerns them, and none waits for the others.
Ordering inside the broker
Changes are applied in log order, and a new image only becomes visible after its delta has been fully applied. A request handler thread therefore never sees half of a change. Consider a leader change: the partition’s entry in the image gains its new leader and epoch in one step, not first the leader and then the epoch. This all-at-once switch is exactly what the immutable image design provides.
When something goes wrong while applying
Occasionally, applying a record fails, for instance because of a full disk when creating a partition folder. A failure in one listener does not stop the image from being updated for the others, and the failure is counted in error metrics so operators can see it. Serious failures make the broker shut itself down rather than continue with a cache that does not match reality, which is safer than serving wrong answers indefinitely.
Many mysterious symptoms turn out to be one listener not keeping up: a partition folder not created, a setting not applied, an access rule not yet active. Knowing that these are separate consumers of the same delta helps you look in the right place.
TThe Life of a Metadata Change: Creating a Topic
Let us follow one small change all the way from a request to every broker’s memory.
An engineer asks for a new topic named orders with 6 partitions and 3 replicas each. This is a typical metadata change, and it touches every stage we have discussed.
sequenceDiagram participant A as Admin client participant B as Any broker participant C as Active controller participant O as Other brokers A->>B: Create topic orders B->>C: Forward the request C->>C: Validate and choose replicas and leaders Note over C: Append topic and partition records to the metadata log C-->>B: Topic created B-->>A: Success C-->>O: Brokers fetch the new records Note over O: Each broker updates its local cache
The admin request reaches any broker
The client contacts whichever broker it knows. That broker does not decide anything itself. It forwards the request to the active controller.
The controller validates
It checks the topic name, that enough brokers exist for 3 replicas, that the requester is allowed to create topics, and that no topic named orders already exists.
It places the replicas
The controller picks brokers for each partition’s replicas, spreading them across brokers and racks, and picks a preferred leader for each.
It writes records to the metadata log
A topic record and six partition records are appended and replicated to a majority of the controller quorum. Once committed, the decision is permanent.
Brokers fetch and apply the records
Each broker’s next fetch returns the new records. It builds a delta, produces a new image and swaps it in. The broker now knows that orders exists.
Chosen brokers create the partitions
Brokers named as replicas notice the new partitions in the delta and create the log folders. Leaders start serving, and followers start fetching.
Clients discover the topic
The next time a producer asks any broker about orders, the answer comes from that broker’s freshly updated cache.
Why the small delay in step 5 is normal
Between the controller committing the change and the last broker applying it, there is a short window, typically a small fraction of a second in a healthy cluster. During it, one broker may already know about orders while another has not heard yet. A client that asks the slower broker gets “unknown topic”. It retries, and the second attempt succeeds. This is the price of caching, and it is why clients are built to retry metadata questions.
LThe Life of a Leader Change
Topic creation is calm. A leader change during a failure is the moment when the metadata cache matters most, and when stale copies do the most damage.
flowchart TD A["Leader broker stops"] --> B["Controller notices missed heartbeats"] B --> C["Controller writes a partition change record"] C --> D["Brokers fetch and apply the record"] D --> E["New leader starts serving, others follow it"] E --> F["Clients get errors then refresh metadata"] F --> G["Clients send traffic to the new leader"]
Suppose Broker 2 leads partition 3 of the orders topic, and it crashes. The controller stops hearing its heartbeats. After the session limit passes, it fences Broker 2 and looks at every partition that Broker 2 led. For partition 3 it picks a new leader from the in-sync replicas, say Broker 1, increases the partition’s leader epoch (a counter of leader changes), and writes a partition change record to the metadata log.
Every broker fetches the record and updates its cache. Broker 1 sees that it is now the leader and begins accepting writes. The other replicas see who the new leader is and start following it. Meanwhile, the producers and consumers that were using Broker 2 discover the problem the hard way: their connections fail or their requests to the wrong broker are refused. They then ask any live broker for fresh metadata and learn that Broker 1 leads partition 3.
| Time | What the controller knows | What brokers know | What a client knows |
|---|---|---|---|
| Before the crash | Broker 2 leads, epoch 7 | Broker 2 leads, epoch 7 | Broker 2 leads, epoch 7 |
| Just after the crash | Broker 2 leads (not yet noticed) | Broker 2 leads | Broker 2 leads, connection fails |
| Controller reacts | Broker 1 leads, epoch 8 | Still epoch 7 until records arrive | Broker 2 leads, retrying |
| Brokers apply the record | Broker 1 leads, epoch 8 | Broker 1 leads, epoch 8 | Refreshing metadata |
| Client refreshed | Broker 1 leads, epoch 8 | Broker 1 leads, epoch 8 | Broker 1 leads, epoch 8 |
A gate is closed because of a fault and the flight moves to a new gate. The operations centre announces it, the boards update within moments, and passengers who already headed to the old gate are redirected by staff. Those with the new gate printed on their boarding pass are already on the way.
The failure-to-recovery time is roughly the time to detect the failure, plus the time for the controller to decide and commit, plus the time for brokers to apply the record, plus the time for clients to notice and refresh. Each stage has settings you can tune, and we will meet them in the chapters ahead.
UWhat a Broker Does With Its Cache
The cache is not just something brokers hold. It is consulted at nearly every step of handling a request.
Metadata requests
A client asks about some topics or all topics. The broker builds the answer purely from its cache: brokers, partitions, leaders, epochs, replicas and ISR. No call to the controller is needed.
Produce and fetch checks
On each produce request, the broker checks it is the leader for that partition and that the client’s view of the leader epoch is not out of date. A wrong broker refuses, with a clear error.
Permission checks
Access rules are held in the cache too, so deciding whether a client may write to a topic is a memory lookup, not a network call.
Quotas and configs
Traffic limits and per-topic settings such as maximum message size come from the same cached picture, and take effect as soon as the updated record is applied.
Finding coordinators
Consumer groups and transactions have a coordinator broker chosen by hashing into an internal topic. Locating it uses the cached partition and leader information for that topic.
Follower behaviour
Followers use the cached leader information to know whom to fetch from, and switch when the cache shows a new leader.
What a metadata answer contains
A response to a Metadata request lists the live brokers with their addresses, then for each requested topic it lists every partition together with the id of its leader, the leader epoch, the replica list, the in-sync replica list, and any replicas that are offline. The cluster identifier is included as well. Since the response is built directly from the broker’s own image, it always reflects exactly what that broker knew at that moment, no more and no less.
| Field in the answer | Purpose for the client |
|---|---|
| Broker id, host and port | Where to open connections |
| Partition count per topic | Needed to hash keys to partitions consistently |
| Leader id per partition | Where to send produce and fetch requests |
| Leader epoch per partition | Lets the client tell fresh answers from stale ones |
| Replica and ISR lists | Used by tools, and by consumers reading from nearby replicas |
| Offline replicas | Shows which copies are currently unavailable |
Because every broker keeps the full cluster picture, a client may ask any of them. That is why the bootstrap list in a client configuration only needs a few addresses. The client learns the rest of the cluster from the first answer it gets.
FFollowing One Produce Request Through Both Caches
To tie everything together, let us watch a single message from the moment a brand-new application starts, and see which cache is used at each step.
An order service starts up for the first time. It has never heard of the cluster, apart from three bootstrap addresses in its configuration. It is about to send its first message to the orders topic.
Cold start: the client cache is empty
The producer knows only the bootstrap addresses. It cannot pick a partition or a leader, since it has no partition count and no leader information.
It asks a bootstrap broker for metadata
The broker answers from its metadata image with the broker list, and for orders, its twelve partitions with leaders, epochs and replicas.
The producer fills its own cache
It stores the answer with a timestamp. It now knows the partition count and where each leader lives, and can open connections to the right brokers.
It chooses a partition
For a message with a key, it hashes the key and uses the partition count from its cache to select one. The choice is purely local, with no network call.
It sends the batch to the leader
The request carries the partition and the leader epoch from the client’s cache.
The broker checks its own image
It verifies it is the leader, that the epoch matches, that the topic exists, and that this client is allowed to write. All four checks read the same in-memory image.
The message is appended and acknowledged
The metadata is no longer involved. The rest is the replication story: appending to the log and waiting for followers.
| Step | Which cache is read | Network call needed? |
|---|---|---|
| Deciding the partition | Client cache (partition count) | No |
| Finding the leader | Client cache (leader and epoch) | No, after the first fill |
| Checking leadership and epoch | Broker image | No |
| Checking permission | Broker image (access rules) | No |
| Applying quotas and topic settings | Broker image (configs and quotas) | No |
| Refreshing after an error or timeout | Any broker’s image, through a new Metadata request | Yes, but rarely |
Counting the network calls
For the first message, the producer makes one extra call to fetch metadata. For the next million messages to the same topic, it makes none, apart from an occasional timed refresh. That is the entire payoff of the two-layer cache: an expensive question asked once and then answered from memory, millions of times, on both sides of the connection.
SThe Second Cache: Inside Producers and Consumers
Clients do not ask a broker about metadata for every message. They keep a private copy and refresh it on a schedule and whenever something goes wrong.
flowchart TD
S["Client needs the leader of a partition"] --> Q{"Is it in the local cache and still fresh?"}
Q -->|"Yes"| U["Use it right away"]
Q -->|"No, or an error occurred"| R["Send a Metadata request to any known broker"]
R --> V["Update the local cache"]
V --> T["Retry the original action"]
When a producer sends its first message to a topic, it does not yet know the partitions or leaders. It sends a Metadata request to a bootstrap broker, stores the answer, and sends the message to the correct leader. All later messages to that topic use the stored answer, at no extra network cost. Consumers do the same, and also use the answer to find the coordinator for their consumer group.
The knobs that control freshness
| Setting | Meaning | Default |
|---|---|---|
| metadata.max.age.ms | Refresh the cache at least this often, even if nothing seems wrong | 5 minutes |
| metadata.max.idle.ms (producer) | Forget a topic from the cache after it has not been used for this long | 5 minutes |
| max.block.ms (producer) | How long a send may wait for metadata before failing | 60 seconds |
| retry.backoff.ms | Pause before retrying after a failure, so clients do not hammer brokers | 100 ms |
| bootstrap.servers | Addresses used to find the cluster the first time | None, must be set |
What triggers an early refresh
The cache grows old
After the maximum age passes, the next operation triggers a refresh even if all seems well. This is a safety net against missed changes.
A broker says “not me”
Errors such as not-leader or unknown-topic-or-partition tell the client its copy is wrong. It refreshes at once, with a short backoff.
A connection fails
If the broker a client was talking to disappears, the client marks its metadata as suspect and refreshes through a different broker.
An unknown topic is used
The first send to a topic not in the cache triggers a fetch of that topic’s metadata, and the send waits for it.
A passenger writes the gate on their boarding pass and walks straight there. They only look at a departure board again if an announcement is made, if the gate turns out to be wrong, or if it has been a long time since they last checked. Constantly re-checking the board would waste everyone’s time.
A producer decides the partition for a keyed message by hashing the key and taking it modulo the partition count. The count comes from the client’s cached metadata. If partitions are added to a topic, producers keep using the old count until they refresh, and the same key can land in a different partition afterwards. Order for that key across the change is not preserved.
GConsumer Groups, Coordinators and Metadata
Consumers use metadata for more than finding partition leaders. Their teamwork, called a consumer group, depends on it too.
Several consumers can share the work of reading a topic by joining a consumer group. Kafka spreads the topic’s partitions among the members so that each partition is read by exactly one member of the group at a time. A special broker, the group coordinator, keeps track of who is in the group, receives their heartbeats, and starts a rebalance whenever the membership or the list of partitions changes.
Finding the coordinator
Which broker is the coordinator for a given group? Kafka answers with a simple rule. The group name is hashed to pick one partition of an internal topic that stores group progress. The broker that leads that partition is the coordinator for that group. A consumer sends a find-coordinator request to any broker, and the broker works out the answer using its metadata image: the partition count of the internal topic and the current leader of the chosen partition.
A large event has many registration desks. Your surname decides which desk handles you, and which staff member is on that desk today is written on the board. If the staff member changes, the board changes, and you are simply told to go to the new person the next time you visit.
The consumer starts and asks any broker for the coordinator
The broker hashes the group name to an internal partition and looks up its leader in the cached metadata.
The consumer joins the group
It connects to the coordinator and announces which topics it wants to read.
Partitions are assigned
An assignment is worked out using the partition lists of the subscribed topics, which come from cluster metadata. Every member receives its share.
Consumers fetch from the partition leaders
Each member finds its partitions’ leaders through its own cached metadata, and starts reading.
Heartbeats continue
Members send regular heartbeats. If one stops, the coordinator removes it and starts a rebalance.
When metadata changes cause rebalances
Partitions are added to a topic
The consumers’ cached metadata eventually shows the new partitions. The group must reassign so that someone reads them.
A new topic matches a pattern
A group subscribed by a regular expression notices the new topic at its next metadata refresh and rebalances to include it.
The coordinator moves
If the leader of the internal partition changes, consumers receive a not-coordinator error, look up the new coordinator through metadata, and reconnect.
A member leaves or joins
Deployments and crashes change membership. Not a metadata change, but the reassignment then reads fresh cluster metadata.
Newer group protocols
Recent Kafka versions include a newer consumer group protocol in which the coordinator broker computes assignments on the server side, instead of one client doing it. With that design, the broker relies on its own cached view of topics and partitions when deciding who gets what, and changes in metadata reach the group through the broker’s normal updates rather than through each client’s refresh timer. The core idea stays the same: assignment is only as good as the metadata behind it.
The transaction coordinator is found in a similar way, by hashing the transactional id onto a partition of an internal topic and looking up its leader in the metadata. The lesson is general: whenever Kafka needs to name “the broker responsible for X”, it turns X into a partition and asks the metadata who leads it.
Frequent rebalances pause consumption while partitions are reassigned. Sources include short session timeouts, slow processing that misses the poll deadline, broad regex subscriptions on busy clusters, and repeated coordinator moves. Stable metadata makes for stable groups.
ELeader Epochs: Telling Fresh Metadata From Stale
Two copies of the metadata can disagree. Leader epochs give Kafka a simple way to decide which one is newer.
A leader epoch is a number attached to each partition that increases by one every time leadership changes. Every metadata answer includes it, and every produce or fetch request from a modern client includes the epoch the client believes is current. This small number solves two problems at once.
A client has old metadata
It sends a request stamped with epoch 7 to a broker that knows epoch 8 is current. The broker can see immediately that the client is behind and reply with a specific error, so the client knows to refresh.
A broker has old metadata
A client holding epoch 8 asks a broker that has only seen epoch 7. The broker can tell it is behind and reply that it does not yet know the epoch, so the client tries again shortly instead of trusting it.
sequenceDiagram participant P as Producer with an old cache participant Old as Former leader participant New as New leader P->>Old: Produce to partition 3, leader epoch 7 Old-->>P: Not the leader, newer leader information attached P->>New: Produce again with leader epoch 8 New-->>P: Success Note over P: The client cache now says epoch 8
Ignoring answers that are older than what we have
Clients use epochs in one more way. When a Metadata response arrives, the client compares each partition’s epoch with the one it already stored. If the new answer carries a lower epoch, it came from a broker that is lagging behind, and the client keeps its own newer information instead of going backwards. Without this rule, a client that happened to ask a slow broker could replace a correct cache with an outdated one.
Newer Kafka versions go a step further. When a broker refuses a produce or fetch request because it is not the leader, it can attach the identity and epoch of the leader it knows about within the error response. The client can then use that immediately without a separate Metadata round trip, which shortens recovery after a leader change.
Leader epochs also protect replication, where followers use them to decide how far to truncate their logs after a leader change. Whether the subject is metadata or data, the epoch is the same tool: a simple counter that lets any two parties agree on which view of the world is newer.
CStaleness, Consistency and the Errors You Will See
Cached metadata is eventually consistent. Copies may briefly differ, but they always converge. Kafka expresses each kind of disagreement as a specific error code.
Eventual consistency sounds abstract until you see the errors it produces. Every error below is a normal part of life on a busy cluster. A handful per minute during a change is expected. A constant stream is a sign that something needs attention.
| Error | Plain meaning | Usual cause | What the client does |
|---|---|---|---|
| NOT_LEADER_OR_FOLLOWER | This broker is not the leader for that partition | Leader moved and the client cache is old | Refresh metadata and retry |
| UNKNOWN_TOPIC_OR_PARTITION | This broker does not know that topic or partition | Very new topic not yet applied, or a typo, or deletion | Retry briefly, then fail if it persists |
| LEADER_NOT_AVAILABLE | A leader is being elected right now | Broker failure or new topic being set up | Wait a short time and retry |
| FENCED_LEADER_EPOCH | The request carries an epoch older than the broker knows | Client cache is behind | Refresh metadata and retry |
| UNKNOWN_LEADER_EPOCH | The request carries an epoch newer than the broker knows | Broker cache is behind | Retry after a short delay |
| TOPIC_AUTHORIZATION_FAILED | Client is not allowed to see or use that topic | Missing access rule | Fail, since retries will not help |
The client-side timeout
The most common metadata problem in application logs is a producer failing with a message that the topic was not present in the metadata after a set time, typically 60 seconds. It means the producer asked for metadata, waited up to its blocking limit, and never received a usable answer for that topic. Typical causes are a wrong topic name with auto-creation disabled, a lack of permission to describe the topic, a bootstrap address that leads nowhere, or an entire cluster being unreachable.
Check the address
Confirm that at least one bootstrap address works from where the client is running, including DNS names and firewalls.
Check the topic
Confirm the topic exists, its name has no typo, and it has partitions with leaders.
Check permissions
A client that lacks permission to describe a topic is told it does not exist or is not authorized, so review access rules.
Check the returned addresses
Even when the bootstrap works, the metadata may return addresses the client cannot reach. That leads to the topic of the next chapter.
Errors that start with “not leader”, “unknown epoch” or “leader not available” are meant to be retried, and Kafka clients do so automatically. Design your application to allow enough time for retries during a leader change, rather than failing at the first sign of one.
AAdvertised Listeners: The Addresses Inside the Metadata
Metadata does not only say who leads a partition. It also says how to reach that leader. When the addresses are wrong, everything looks fine at first and then fails.
A broker may be reachable on several networks at once: a private network for other brokers, an internal network for applications in the same data centre, and a public address for outside clients. Kafka models each as a named listener. The setting called listeners tells the broker which addresses and ports to open. The setting called advertised.listeners tells the broker which addresses to announce to clients through the metadata, and these can be different.
A shop has a back door for deliveries and a front door for customers. The shop’s public sign must list the front door address. If it lists the back door, or a street name only staff understand, customers arrive at the wrong place even though the shop is open and the sign looks official.
How the connection flows
Client connects to a bootstrap address
This is the address written in the client’s configuration. Only this first step uses it.
The broker answers from its cache
It returns the broker list with the address for the listener on which the client connected, taken from the advertised settings.
The client connects to the leaders directly
For each partition it uses, the client opens a connection to the address in the metadata, which may be a different broker from the bootstrap one.
Traffic flows, or fails
If the advertised address is not reachable from the client, the data step times out even though the bootstrap step worked.
The classic mistake
A team moves Kafka from a laptop to a cloud server. The bootstrap address is the server’s public name, so the client connects and receives metadata. But the broker still advertises its internal machine name, which only makes sense inside the cloud network. The client sees the leader’s address, cannot resolve or reach it, and every send times out. The broker is healthy and the topic exists, yet nothing flows, because the map the client was given points to an address it cannot use.
| Symptom | Likely cause | Fix |
|---|---|---|
| Connects to bootstrap, then times out on send | Advertised address unreachable from the client | Advertise an address the client can reach |
| Works inside the network, fails from outside | Only an internal listener is advertised | Add an external listener with its own advertised address |
| Client cannot resolve the hostname | Advertised name is not in the client’s DNS | Use names that resolve for that client |
| Listener not found error | Client connected on a listener the broker does not advertise | Check listener names and security protocol mapping |
A successful first connection only proves the bootstrap address works. Always test that the addresses returned inside the metadata are also reachable from the client’s network.
TAuto Topic Creation and Metadata Requests
A Metadata request can do more than ask. Under some settings, asking about a topic that does not exist makes Kafka create it.
Brokers have a setting called auto.create.topics.enable. When it is on, a client that asks for metadata about an unknown topic, or sends a message to one, can cause that topic to be created with default settings. The Metadata request carries a flag saying whether the client allows this. Clients can turn it off with their own setting, so that an accidental typo does not create a stray topic.
Why teams like it
- Convenient in development and testing
- No separate setup step for new topics
- Applications can start before an operator prepares the topic
Why production teams avoid it
- A typo silently creates a wrong topic with default partitions
- Defaults may not suit the workload
- Any client with basic access can create topics
- Metadata questions have side effects, which is surprising
What happens behind the scenes
When a broker receives a Metadata request for an unknown topic with auto-creation allowed, it does not create the topic itself. It forwards a creation request to the active controller, just like an administrator’s request, following the steps in Chapter 12. The first answer to the client is usually “leader not available”, because the topic’s partitions do not have leaders yet. The client retries after its backoff, and soon receives real metadata. This explains a familiar pattern in logs: one warning about leader not available on the very first send to a new topic, followed by normal operation.
Turn auto-creation off on the brokers, create topics deliberately with the intended partition count, replication factor and settings, and keep those definitions in version control. It removes a whole class of surprises and keeps the metadata tidy.
The cost of a typo
A service writes to a topic named payments-events but a configuration file says paymnets-events. With auto-creation on, Kafka happily creates the misspelt topic with default settings. Events pile up unread, and nothing fails. With auto-creation off, the first send fails loudly with a metadata timeout, and the mistake is caught within minutes.
FFour Failure Stories With Real Numbers
The best way to feel how the metadata cache behaves is to watch it under pressure. These four stories use plausible numbers to show the mechanics.
Story 1: the reconnection storm
A cluster of six brokers serves 20,000 client connections across many applications. Broker 4 crashes, and it led about 500 partitions. The controller notices after the heartbeat limit of roughly nine seconds, moves the leaders and records the changes. Within a second or two, all brokers have applied the change. But in the meantime, thousands of clients with connections to Broker 4 saw errors and each sent a Metadata request to a surviving broker.
Here the design pays off. Every broker answers from memory, so the flood is spread over five machines and handled quickly. Clients also wait a short backoff before retrying, so their requests do not all arrive in the same instant. Most applications recover in a few seconds beyond the detection time. If clients had no backoff, or if every metadata answer required a call to the controller, the same event could turn into a second, self-made outage.
Most of the recovery time is detection time. Tuning heartbeat and session settings trades speed against false alarms. Too aggressive, and a brief pause fences a healthy broker.
Story 2: the broker that fell behind
Broker 2 shares a disk with a noisy neighbour, and its disk stalls for twenty seconds. During that time, the controller commits a leader change for a partition that Broker 2 used to lead. Broker 2 cannot apply the record yet, so for a while its cache still says it is the leader. A producer with an old cache sends it a message with the old epoch. Broker 2 has not yet learned the new epoch, so it cannot yet reject the message on epoch grounds alone.
What saves the day is layered protection. First, Broker 2 has stopped sending heartbeats normally, so the controller fences it and moves leadership away. Second, when Broker 2 does catch up and applies the record, it stops accepting writes for that partition. Third, replicas follow the new leader, so an old leader’s late writes cannot be committed by the in-sync replicas. The metadata cache is one line of defence, and the epoch checks in the replication protocol are another.
Story 3: doubling the partitions
An orders topic has 6 partitions and the team needs more throughput, so they raise it to 12. The controller commits the change instantly and all brokers apply it within a second. But producers still hold cached metadata that says 6 partitions, and they keep hashing keys modulo 6 until their next refresh, which can take up to the maximum metadata age of five minutes by default.
Once each producer refreshes, it starts hashing modulo 12, and a customer whose orders used to go to partition 2 may now go to partition 8. Consumers reading partition 2 have older messages for that customer, and consumers of partition 8 have newer ones. Order across the boundary is lost. Consumers subscribed by pattern also notice new partitions only at their own refresh interval. The lesson is to plan partition counts up front, and to treat an increase as an event that can break per-key ordering.
Story 4: the controller failover
The active controller crashes. The remaining controllers hold an election, and one of them becomes active, typically within a few seconds. During this window, no new decisions can be made. Nobody can create a topic, and if a broker fails during the window, its leaders cannot be moved until the new controller takes over.
Yet ordinary produce and consume traffic continues without any interruption, since brokers serve requests from their cached metadata and do not need the controller for the data path. When the new active controller comes up, brokers reconnect to it and continue fetching the log from where they stopped. Because the log is replicated to a majority before any decision counts, the new controller holds every committed decision, so nothing already announced is lost.
| Story | What was stale | User-visible effect | Main lesson |
|---|---|---|---|
| Reconnection storm | Client caches pointing to a dead broker | Seconds of retries | Cache answers plus backoff absorb storms |
| Lagging broker | One broker’s cache | A few rejected or delayed requests | Fencing and epochs protect correctness |
| Doubling partitions | Client partition count | Per-key order breaks at the switch | Plan partition counts early |
| Controller failover | Nothing on the data path | Brief pause in administrative changes | Data path does not depend on the controller |
PPerformance and Scalability of the Metadata Cache
The cache is fast because it lives in memory, but it is not free. Its cost grows with the size of the cluster and the behaviour of its clients.
Every partition of every topic has an entry in the cache: its leader, epoch, replica list and ISR. A cluster with a few thousand partitions has a trivial cache. A cluster with hundreds of thousands has one measured in tens or hundreds of megabytes, and every broker holds a full copy. That is still small next to the message data, but it is not nothing, and other costs grow with it.
The all-topics request
A Metadata request can name specific topics or ask for all of them. The second form is convenient and expensive. On a big cluster, the answer lists every partition of every topic. As a rough illustration, if each partition entry takes around a hundred bytes in the response, a cluster with 200,000 partitions would send about twenty megabytes for a single request. If hundreds of monitoring tools, dashboards or pattern-subscribed consumers make that request every few minutes, brokers spend much of their time building and sending enormous answers.
Ask only for what you need
Producers request just the topics they write to. Applications that list all topics on every poll should be redesigned to cache the list.
Avoid broad regex subscriptions
A consumer subscribed by pattern must fetch broad metadata regularly to spot new matches. Prefer explicit topic lists on large clusters.
Slow the monitoring pollers
Dashboards and admin scripts that describe the whole cluster each second add load. Poll at a sensible interval, or read metrics instead.
Keep partition counts sensible
More partitions bring more parallelism but also a larger cache, longer startup, bigger answers and more work per change.
Why the modern design scales better
The older push-based design had each metadata change sent from the controller to every broker, and a new controller had to reload everything from ZooKeeper. The modern log-based design changes both. Changes are small records that brokers pull, and a new active controller already holds the whole log in memory, since standby controllers apply it continuously. Also, applying a delta to an immutable image shares all unchanged parts, so a single change to one partition does not copy the entire cluster picture. One goal of the design was to allow clusters with a very large number of partitions, in the order of millions, with fast controller failover.
A shorter metadata.max.age.ms means clients notice silent changes sooner but generate more metadata traffic. A longer one saves traffic but leaves stale data around longer if an error does not trigger an early refresh. The default of five minutes suits most systems, because errors already trigger prompt refreshes when it matters.
CConsistency and the Trade-offs Behind the Design
Kafka could have chosen a different way of sharing the cluster picture. Comparing the alternatives shows why this one was chosen, and what it costs.
| Approach | How it works | Strength | Weakness |
|---|---|---|---|
| Ask the controller every time | Each request triggers a lookup at one central service | Always up to date | Bottleneck, slow, single point of failure |
| Broker cache from an ordered log (Kafka) | Brokers apply the same records in the same order | Fast, scalable, all brokers converge | Brief staleness is possible |
| Static configuration on clients | Clients hard-code where everything lives | No lookups at all | Cannot react to failures or growth |
| Client-side only discovery | Clients probe brokers to find leaders by trial | No cluster-wide state | Wasted requests, slow recovery |
Kafka’s choice gives strong ordering of changes but not instant delivery to every reader. All brokers apply the same records in the same order, so they never disagree about the sequence of events, only about how far along the sequence they have reached. Since a leader epoch accompanies every change, any request can be judged as fresh or stale, and mistakes are corrected without harm.
Boards showing the departure list are guaranteed to show the list in the right order, even if one board is a second slower than the others. Nobody ever sees gate 7 assigned before gate 6 was cleared, but somebody might briefly read yesterday’s gate for a flight that has just moved.
Where CAP shows up
When the network splits, a system must choose between staying consistent and staying available. For metadata, Kafka chooses consistency for decisions: changes need a majority of the controller quorum, so a minority partition cannot create topics or elect leaders. For reads, it favours availability: brokers keep answering from their last known cache during the split. The result is a cluster that will not make conflicting decisions but will continue serving the existing traffic.
What this design guarantees
- All brokers see changes in the same order
- No two active controllers make conflicting decisions
- Reads continue during controller trouble
- Stale requests are detected by epochs
What it does not guarantee
- That every broker has the latest change at the same instant
- That a client’s private cache is current
- Administrative changes during a lost majority
- Ordering of keys across a partition-count change
SSecurity Around Metadata
Metadata may look harmless, but a list of topics, partitions and addresses is exactly what an attacker wants to see first.
Topic names often reveal business activity: a topic called merger-negotiations or customer-refunds tells a story. Broker addresses reveal network layout. Access rules define who can do what. Because all of this sits in the metadata cache, it deserves protection just like the messages.
Filtered answers
A broker only includes topics the caller is allowed to describe. Clients without permission are told the topic is not authorized or is unknown, not shown its details.
Authenticate before answering
Use TLS and a login method such as SASL or mutual certificates, so anonymous callers cannot request metadata from the cluster at all.
Separate controller network
The controller quorum should be reachable only by brokers and administrators, on its own listener, never by ordinary applications.
Advertise only what clients need
Do not expose internal hostnames or private topology through the advertised listeners of an externally reachable listener.
Permission checks come from the cache
In KRaft clusters, access rules are stored in the metadata log and every broker keeps them in memory. This makes checks very fast, but it also means a rule change reaches each broker when that broker applies the record. In the short window before that, a broker may still use the old rule. For most systems this is invisible, but for urgent revocations, such as removing a compromised account, it helps to verify that all brokers have applied the change, and to disable the credential at the identity source too.
Some authorizer settings allow everyone when no rule exists for a resource. On a shared cluster, such a setting means new topics are readable by anyone until rules are added. Prefer denying by default and granting deliberately.
MMonitoring the Metadata Cache
A healthy cache is one that is current, quiet and fast. A few metrics tell you whether it is all three.
| Metric | What it tells you | What to do |
|---|---|---|
| Last applied metadata record offset and lag | How far this broker’s cache trails the log | Alert on lag that keeps growing, since it means stale answers |
| Metadata load and apply error counts | Records the broker failed to process | Any non-zero value needs investigation |
| Active controller count | How many controllers claim to be active | Exactly one is healthy. Zero or two is an emergency |
| Fenced broker count | Brokers currently held out of service | Alert if it stays above zero after maintenance |
| Global topic and partition counts | Overall size of the cluster picture | Track growth to plan capacity |
| Metadata request rate and time | How often clients ask and how long answers take | Spikes point to broad or badly behaved clients |
| Request errors by type | How often clients hit not-leader or unknown-topic errors | Bursts after changes are normal. Constant streams are not |
| Producer metadata age | How old the producer’s cached metadata is | Values far above the refresh interval suggest refresh is failing |
A simple diagnosis path
Clients report not-leader errors
Check whether a leader change happened recently. A short burst that fades is normal. A steady stream is not.
Compare the brokers’ applied positions
Find out whether one broker’s metadata lag is much larger than the others. That broker is answering with old facts.
Inspect the slow broker
Look at its disk latency, CPU, garbage collection and network path to the controller. One of these is usually the cause.
Check the client side
If brokers agree with each other, examine client versions, bootstrap lists and advertised addresses, since the fault may be in the client’s private copy.
Exact metric names and groupings change between Kafka releases. Confirm the current names in the documentation for your version, and build dashboards around the ideas above rather than a fixed list of names.
RReal-World Usage
Companies running Kafka at scale meet the metadata cache in daily life, mostly through its side effects.
LinkedIn: where the pressure began
Kafka was created at LinkedIn, which has publicly described running very large clusters with a huge number of partitions. Growth in partition counts is what made the older ZooKeeper-based design painful: slower controller failovers and heavier state to reload. The community’s work to move cluster state into Kafka’s own log, and to keep every broker’s cache current from it, grew directly out of the needs of operators at this size.
Uber: many clients, constant change
Uber has written publicly about running Kafka as a backbone for a large number of services, with thousands of applications producing and consuming. In an environment like that, deployments happen all day and clients start and stop constantly. Every start is a metadata request, and every broker maintenance is a wave of refreshes. Fast in-memory answers and client backoff are what keep such churn from turning into an overload.
Cloud-managed Kafka services
Managed Kafka offerings from cloud providers and vendors put brokers behind load balancers and private networks. Their most common support issue is exactly the advertised-listener problem from Chapter 20: the bootstrap address works, but the addresses returned in metadata are unreachable. Good documentation for these services spends a lot of space explaining which addresses to advertise for each network path.
An illustration: a retail platform on sale day
This is a simplified illustration, not a description of any specific company. A retailer runs a promotion that multiplies traffic by ten. Autoscaling starts three hundred new application instances in a few minutes. Each instance opens a producer, requests metadata for two topics, and starts sending. Because the brokers answer from memory and each client asks for only the topics it needs, the added load is trivial. In contrast, an old analytics tool that requests all topics every few seconds adds far more load than the three hundred new instances combined.
Metadata caching seldom causes headlines. When it does, the cause is almost always one of three things: addresses that clients cannot reach, clients that ask for far more than they need, or a broker that has fallen behind its log.
DDesign Patterns and Anti-patterns
A few habits keep the metadata path healthy. A few shortcuts quietly damage it.
Patterns that work
Several bootstrap addresses
List at least two or three brokers, ideally in different racks or zones, so a client can start even if one broker is down. A load-balanced name in front of the brokers is also common.
Named listeners per network
Give each network path its own listener and advertised address: internal, external, and so on. Each client then gets addresses it can reach.
Request only needed topics
Keep metadata requests narrow so answers stay small, and cache lists of topics in tools instead of refetching them.
Explicit topic creation
Create topics through reviewed scripts or infrastructure code, with the right partition count and settings from day one.
Dedicated controllers on larger clusters
Run controller nodes separately from brokers when a cluster is big, so heavy data traffic cannot slow down decision making.
Tolerant retry settings
Allow enough time and retries for a leader change to complete and be discovered, rather than failing at the first error.
Anti-patterns to avoid
What people do
They leave the advertised address as an internal machine name after moving Kafka to a new network.
Why it hurts
Clients connect to the bootstrap address, get metadata, and then fail to reach the advertised leaders. The problem looks like a broken broker or a network fault, and can take hours to diagnose.
Do this instead
Advertise addresses that each client group can actually reach, and test from each network with a real produce and consume, not only a connection check.
What people do
They build monitoring tools that request the complete list of topics and partitions every second.
Why it hurts
Each request forces brokers to build and send the entire cluster picture. On a large cluster this wastes CPU and network and slows real traffic.
Do this instead
Poll at a relaxed interval, request only what is needed, or rely on metrics that brokers already publish.
What people do
They increase a topic’s partition count casually on a topic where message order per key matters.
Why it hurts
Producers switch to the new count as they refresh their caches, so the same key begins to land on a different partition and per-key order breaks at that moment.
Do this instead
Choose partition counts with room to grow. When you must change them, plan for it: pause producers, drain consumers, or create a new topic and migrate.
What people do
They hard-code a single broker as the only bootstrap server.
Why it hurts
If that one broker is down when a client starts, the client cannot find the cluster even though every other broker is healthy.
Do this instead
List several brokers, or a name that resolves to several of them, so the first step always has a working option.
BBest Practices and Common Mistakes
A short checklist for reviews and a table of mistakes that keep coming back.
Test the full path from every network
For each group of clients, verify that the bootstrap works and that the addresses inside the metadata are reachable, using a real send and receive.
Keep clients reasonably modern
Newer clients handle leader changes faster, use leader epochs and receive leader hints in error replies. Old clients need more refreshes to recover.
Treat topic creation as a deliberate act
Turn off automatic creation in production, and create topics with reviewed settings.
Watch the applied position of every broker
A broker that is up but behind its log is quietly serving old facts. Alert on lag, not only on liveness.
Restart brokers one at a time
Wait for the broker to be unfenced and caught up before moving to the next, so the cluster never loses too many good caches at once.
Rehearse failures
Stop a broker in a test cluster and watch client errors, refreshes and recovery time. Teams that have seen it once respond calmly in production.
| Common mistake | What goes wrong | Better approach |
|---|---|---|
| Testing only the bootstrap connection | Data path fails later on unreachable advertised addresses | Test a real produce and consume from the client network |
| Assuming a new topic is instantly visible everywhere | Brief unknown-topic errors right after creation | Allow a short retry period after creating a topic |
| Setting a very long metadata age to cut traffic | Silent changes noticed late | Keep the default unless measurements say otherwise |
| Setting a very short metadata age | Needless request load on brokers | Rely on error-driven refreshes for urgent changes |
| Ignoring controller health | Administrative changes stall unnoticed | Monitor active controller count and quorum health |
| Auto-creating topics in production | Typos create stray topics with default settings | Disable it and create topics deliberately |
MMyths, Traps and Gotchas
Some beliefs about metadata caching sound sensible and are wrong. Here are the most common.
“Clients ask the controller for metadata”
Clients ask any broker. Brokers answer from their own cache. Clients normally never talk to the controller directly.
“If the controller is down, Kafka is down”
Producing and consuming continue from cached metadata. What stops is administrative change and leader elections until a controller is active again.
“The bootstrap server is the only server used”
It is used just to discover the cluster. After that, clients talk directly to whichever brokers lead their partitions.
“All broker caches are identical at every moment”
They converge quickly, but at any instant one broker may be a few records behind another. That is normal and handled with epochs and retries.
“More frequent refresh is always safer”
Beyond a point it just adds load. Error-driven refreshes already cover urgent cases, and the timer is only a backstop.
“The metadata cache stores messages”
It holds descriptions of the cluster only. Message data lives in partition logs and, for speed, in the operating system’s page cache.
“Unknown topic always means the topic is missing”
It can also mean the broker has not yet applied a fresh creation record, or that the caller lacks permission to see the topic.
“Adding partitions is harmless”
It is safe for data, but changes how keys map to partitions once clients refresh, which can break per-key ordering.
Gotchas worth remembering
Many articles, and many answers in old forums, describe ZooKeeper-era behaviour. Check that any advice about controllers, UpdateMetadata requests or ZooKeeper paths applies to the version you actually run.
A load balancer in front of brokers works for the bootstrap step, but the metadata still returns direct broker addresses. Unless every advertised address goes through the balancer too, clients will bypass it or fail.
The all-topics request that was harmless on a small cluster can become a serious load after a cluster grows a hundredfold. Revisit tools written years ago when the cluster was small.
PPutting It Together: Tuning for Three Scenarios
Let us apply what we have learned to three different workloads. The method matters more than the specific numbers.
Ask four questions for any system. How quickly must clients recover from a leader change? How many clients and partitions exist? Do clients connect from several networks? And how costly is a mistake in topic setup? The answers point to different choices for the metadata path.
| Scenario (illustration) | Main concern | Client side choices | Cluster side choices |
|---|---|---|---|
| Payments platform, moderate size | Fast, safe recovery and no stray topics | Modern clients, generous retry time, several bootstrap brokers | Auto-creation off, strict access rules, dedicated controllers |
| Large shared cluster, thousands of clients | Metadata load and noisy neighbours | Narrow metadata requests, no broad patterns, default refresh interval | Watch request rates, cap partition growth, review tools that poll everything |
| Hybrid setup: apps in the cloud and on premises | Reachable addresses for every network | Each client group uses its own listener and bootstrap name | Separate listeners and advertised addresses per network |
Map the networks
List every place clients run from, and decide which address each of them should receive in the metadata.
Estimate the size
Count topics, partitions and clients. Ask whether any tool requests all topics, and how often.
Set recovery expectations
Decide how long an application may spend retrying during a leader change, and set timeouts to match.
Lock down creation and access
Turn off automatic creation and review who can describe and create topics.
Rehearse and measure
Stop a broker, watch the errors and refreshes, and record how long recovery takes. Adjust from evidence, not guesses.
A quick sanity check for any design
Ask aloud: “If one broker disappears right now, how does each of my clients learn about the new leader, and how long will that take?” If the team can answer with specific settings and observed timings, the metadata path is understood. If the answer is “it should just work”, it is worth a rehearsal.
FFrequently Asked Questions
Short answers to questions that come up again and again.
SSummary and Key Takeaways
A cache of the cluster’s map, kept in every broker and every client, is what lets Kafka answer “who leads this partition?” in microseconds.
The controller decides, the metadata log records, and each broker turns those records into an in-memory picture that it can read without locks or network calls. Clients copy the parts they need and refresh them on a timer, after errors, or when they meet something new. Leader epochs let every party tell fresh from stale, so the occasional disagreement is corrected quickly and safely. Understanding this chain explains both the speed of Kafka and the handful of surprises that come with it: unknown topics right after creation, unreachable advertised addresses, and keys that move when partitions grow.
Key Takeaways
- Every broker holds the whole cluster picture: topics, partitions, leaders, epochs, replicas, configs and access rules, so any broker can answer any client.
- The controller decides and the log records. In modern Kafka, brokers build their cache by reading a replicated metadata log, applying small deltas to immutable images.
- Clients keep a second cache and refresh it on a timer, after errors, and on first use of a topic. Errors such as not-leader are normal signals, not disasters.
- Leader epochs identify the newer view, so stale requests and stale answers are detected and corrected without harm.
- A broker that falls behind is fenced, so a stale cache cannot serve clients for long. Watch each broker’s applied position, not just its liveness.
- Advertised listeners are part of the metadata. If clients cannot reach the addresses returned, the bootstrap succeeds and the data path fails.
- Keep requests narrow and settings deliberate: ask only for needed topics, create topics explicitly, plan partition counts, and rehearse failures.