Benchmarking Methodology with kafka-producer-perf-test
Anyone can run a load test and get a number. The hard part is getting a number you can trust, explain and repeat. This guide teaches the method: what the tool measures, which settings change the result, how to design fair experiments, and how to turn the numbers into decisions.
Two engineers argue in a meeting. One says, “Our Kafka cluster handles a million messages a second.” The other says, “I measured it last week and got two hundred thousand.” Both are telling the truth. They used different message sizes, different producer settings, different topics and different machines, and they never wrote any of that down.
A benchmark is only useful when it answers a clear question under stated conditions. “How fast is Kafka?” is not a question. “How many 1 KB messages per second can one producer on this machine send to a three-broker cluster with full replication, while keeping the 99th percentile latency under 50 milliseconds?” is a question, and it can be answered.
Kafka ships with a small command-line tool for producing load, called kafka-producer-perf-test. It is simple to run and easy to misuse. This guide explains it from zero, then builds a careful method around it: the settings that matter, how to read the output, how to avoid the classic traps, and how to report results honestly. Every term is explained when it first appears, and each big idea comes with an everyday comparison and worked numbers.
KKafka Producers in Five Minutes
A short refresher on the parts of Kafka that a producer benchmark touches.
Apache Kafka is a system for moving streams of messages between programs. A producer is a program that sends messages. The servers that receive, store and serve them are brokers, and a group of brokers working together is a cluster. Messages are organised into named channels called topics. Each topic is split into partitions, which are ordered lists of messages stored on brokers. Splitting a topic into partitions lets many brokers share the work.
Each partition has one leader broker that handles all reads and writes for it, and copies called replicas on other brokers. Replicas are the safety net: if the leader dies, another replica takes over. When a producer sends a message, it goes to the leader of the right partition, and the leader passes it to the replicas that follow it.
Think of a postal sorting centre. Senders (producers) hand parcels to clerks (brokers). Each region of the country (a partition) has one head clerk (the leader) who logs every parcel, and deputies (replicas) who keep copies of the log. A benchmark asks: how many parcels can senders hand over per minute, and how long does each sender wait for a receipt?
Producer
The program that creates messages and sends them to Kafka.
Broker
A Kafka server that stores partitions and answers requests.
Topic and partition
A topic is the named channel. Its partitions are the parallel lanes inside it.
Replica
A copy of a partition on another broker, kept for safety.
Every one of these parts can be the slowest link. A benchmark result is really the speed of whichever part is the bottleneck under your particular conditions.
WWhy Benchmark at All?
Measuring is how guesses become decisions.
Kafka is used for very different jobs. One team streams tiny click events at huge volume. Another moves large files as messages. A third needs every message safely stored on three machines before it is acknowledged. Each job stresses the system differently, so published numbers from other people’s tests rarely match your case. Benchmarking gives you your own numbers.
Capacity planning
How many brokers, disks and network links do we need for next year’s traffic?
Configuration choices
Does a larger batch size or compression help our workload, and by how much?
Hardware and cloud choices
Is the faster disk type worth its price? Does a different instance type change the ceiling?
Regression checks
Did the new version, new setting or new network rule make things slower?
Understanding limits
Where does the system start to struggle, and what fails first?
Meeting agreed targets
Show that a service level, such as a latency goal, can actually be met.
A car maker crash-tests cars before selling them, not after a customer’s accident. Benchmarking is the crash test for your data pipeline, done in a safe place on purpose.
Even a good test is a simplified copy of production. Its value is in comparisons and in finding limits, not in predicting exact production numbers to the last digit.
TThe Tool: What kafka-producer-perf-test Is
A small program that sends a chosen number of messages as fast, or as slowly, as you ask, and reports how it went.
The tool is a command-line program included with Kafka. It creates a producer, generates messages of a size you choose, sends them to a topic, and prints progress and a final summary. Depending on how Kafka was installed it may be a shell script under the name kafka-producer-perf-test, with a “.sh” ending in plain Apache downloads, and it wraps a Java class inside the Kafka tools. It needs no code from you.
| What you tell it | Typical option name | Purpose |
|---|---|---|
| Where to send | topic, and producer properties such as the bootstrap servers | Choose the topic and cluster to test |
| How many messages | num-records | Total messages to send in the run |
| How big is each | record-size, or a payload file | Message size, or real sample messages |
| How fast | throughput | Target messages per second, or minus one for no limit |
| How the producer behaves | producer-props, or a producer config file | Any producer setting: acks, batching, compression, and so on |
| Extra detail | print-metrics, and transaction options | Print the producer’s internal metrics, or test transactions |
Run the tool with its help option on your own Kafka version to see the exact list. Newer versions have added a few options, so treat any list in an article, including this one, as a guide rather than the final word.
What it is not
What it does well
- Produces steady, repeatable load from one process
- Lets you set any producer property
- Reports throughput and latency percentiles
- Comes with Kafka, so no setup
What it does not do
- Model your real message shapes and key patterns by default
- Test consumers, which have their own tool
- Simulate many different applications at once
- Check that messages arrive correctly at the other end
The tool is like a treadmill for the sending side. It shows how fast the runner can go and how they breathe. It does not tell you how the runner will do on a hilly trail with a backpack, unless you set those conditions up yourself.
AAnatomy of a Run
What happens between pressing Enter and seeing the final line.
flowchart LR G["Message generator
size and payload"] --> P["Producer buffers and batches"] P --> N["Network"] N --> L["Partition leader broker"] L --> R["Follower replicas"] R --> L L -->|"acknowledgement"| P P --> M["Latency and throughput recorded"]
Start and connect
The tool builds a producer from your settings and contacts the cluster to learn where partitions live.
Generate each message
It makes a message of the chosen size, or picks a sample from your payload file.
Hand it to the producer
The producer puts it in a memory buffer, grouped by partition, and waits to form a batch.
Send batches
A background thread sends full or old-enough batches to the partition leaders.
Receive acknowledgements
When the broker confirms, the tool records how long that message took and counts it as sent.
Report progress and finish
Every interval it prints a line for that period, and at the end a summary with totals and percentiles.
Two clocks, two answers
Throughput is how much work is done per second: messages per second, or megabytes per second. Latency is how long one message takes from being sent to being acknowledged. They are related but different. A system can move enormous volume while each message waits a while in a batch. Or it can answer each message very fast while moving less in total. A good benchmark reports both together.
A bus carries many people at once but leaves only when full, so each rider may wait. A taxi leaves at once but carries few. Throughput is people moved per hour. Latency is how long one person waits before setting off.
RReading the Output
The tool prints lines while it runs and a summary at the end. Each number has a precise meaning.
| Field | Meaning | How to use it |
|---|---|---|
| Records sent | Messages completed in the run or interval | Check that it equals the number you asked for |
| Records per second | Messages acknowledged per second | Main throughput measure for small messages |
| Megabytes per second | Payload volume per second | Main throughput measure for large messages and for network limits |
| Average latency | The mean time from send to acknowledgement | Rough picture only, since a few slow messages pull it up |
| Maximum latency | The slowest single message | Reveals pauses, but often one unlucky event |
| 50th percentile | Half of messages were faster than this | The typical experience |
| 95th, 99th percentile | 95 or 99 percent were faster than this | The experience of the slower tail |
| 99.9th percentile | All but the slowest one in a thousand were faster | Shows rare but real delays |
Why percentiles beat averages
Suppose 990 messages take 5 milliseconds and 10 messages take 500 milliseconds. The average is about 10 milliseconds, which sounds fine. But one message in a hundred waited half a second. A user who happens to hit those slow ones sees a slow service. Percentiles show this. The 99th percentile here is close to 500 milliseconds, which tells the truth the average hides.
The tool keeps latency measurements in a bounded structure, so very high percentiles are approximate, especially in very long runs. Treat the 99.9th percentile as a useful hint, not a precise figure, and compare it only between runs of similar length.
A restaurant says its average wait is ten minutes. But if one table in a hundred waits an hour, those guests never come back. The tail matters as much as the average.
MWhat Exactly Is Being Measured
A latency number is only as meaningful as the stopwatch behind it.
In this tool, latency for each message is generally the time between handing the message to the producer and receiving the broker’s acknowledgement in a callback. That includes time waiting in the producer’s buffer for a batch to fill, the network trip, the broker’s work including replication when the settings require it, and the trip back. It does not include the time for a consumer to read the message. So this is send latency, not end-to-end latency.
Waiting in the batch
Time the message sits until its batch is full or the linger time expires.
Network and broker time
Travel to the leader, writing, and replication if required by the acknowledgement setting.
Consumer delay
How long until a reader sees the message. That needs an end-to-end test.
Time blocked before sending
If the producer’s buffer is full, the send call may wait. Depending on the version, that waiting may not appear in the latency numbers, so throughput and latency should be read together.
If the product question is “how soon can a reader see a message”, build an end-to-end test as well. Use the producer tool for the sending side and a matching consumer-side measurement for the reading side.
A courier company can time how long from handing over a parcel to getting a receipt. That is not the same as how long until the recipient opens it. Both are valid times, but they answer different questions.
PProducer Settings That Change the Result
Most of the difference between a slow and a fast run lives in a handful of producer settings.
| Setting | What it does | Effect on results |
|---|---|---|
| acks | How many brokers must confirm before a send counts as done | Zero is fastest and least safe. One waits for the leader. All waits for the in-sync replicas, safest and slower. |
| batch.size | Maximum bytes in one batch for one partition | Larger batches raise throughput and can raise latency |
| linger.ms | How long to wait for more messages before sending a batch | A few milliseconds often improves throughput a lot |
| compression.type | Whether and how batches are compressed | Saves network and disk, costs processor time, effect depends on the data |
| buffer.memory | Total memory for unsent messages | Too small causes the producer to block under load |
| max.in.flight.requests.per.connection | How many requests may be outstanding to one broker | Higher can raise throughput, and interacts with ordering and retries |
| enable.idempotence | Prevents duplicates caused by retries | Small overhead, and it works with certain acks and in-flight limits |
| request and delivery timeouts | How long to wait before failing a send | Affects behaviour under trouble, and the tail of latency |
The batch size is the size of the delivery van, and linger is how long the driver waits for more parcels before leaving. A bigger van and a longer wait moves more parcels per trip but keeps early parcels waiting. Acks is whether the driver must bring back a signed receipt from the recipient’s whole family, or only from the front desk.
Safety settings are performance settings
Choosing acks to be all and enabling idempotence makes messages safer. It also changes speed. A benchmark that uses the fastest, least safe settings will show impressive numbers that production, which needs safety, can never reach. Always test with the durability settings you will actually use.
Some producer defaults, such as acks and idempotence, have changed in recent Kafka releases. Do not assume the default you remember. Print the effective configuration at the start of every benchmark and record it.
BBroker and Topic Settings That Change the Result
The producer is only half the story. The topic you send to shapes the answer.
| Setting | What it controls | Effect on results |
|---|---|---|
| Number of partitions | How many parallel lanes the topic has | More partitions spread load over more brokers and disks, up to a point |
| Replication factor | How many copies exist | More copies mean more network and disk work per message |
| min.insync.replicas | Minimum in-sync copies needed to accept a write with acks all | Affects safety and whether writes stall when a broker is down |
| Broker threads for network and disk | How much parallel work brokers can do | Can shift the bottleneck |
| Log flush and segment settings | How data is written and rolled on disk | Usually left at defaults, since Kafka leans on the operating system’s cache |
| Compression at the topic level | Whether brokers recompress data | Can add broker processor cost if it differs from the producer’s choice |
A topic with one partition is limited by one leader broker, one disk path and one lane. If your real topic has twelve partitions and three brokers, the test topic should look the same. Otherwise the test measures something you do not run.
Do not let the test create a topic by accident with default partitions. Create it with the partition count and replication factor you want to test, and use a fresh topic name for each test series so old data does not distort results.
Testing how fast a supermarket can serve customers with only one checkout open tells you little about a store that opens twelve. Match the number of checkouts to the store you care about.
LThe Latency and Throughput Trade-off
You cannot maximise both at once. Every benchmark chooses a point on a curve.
Batching is the main reason. Waiting to fill a batch moves more data per trip, so throughput rises, but the first message in the batch waits, so latency rises too. Push the load higher and higher and you eventually hit the saturation point, where something in the path is fully busy. Past it, extra load does not raise throughput. It only makes messages queue, and latency climbs sharply.
flowchart LR A["Low load
fast, low throughput"] --> B["Moderate load
good balance"] B --> C["Near saturation
throughput flattens, latency rises"] C --> D["Overload
queues build, latency explodes"]
| Goal | Typical direction | Cost |
|---|---|---|
| Highest throughput | Larger batches, some linger, compression | Higher latency |
| Lowest latency | Small or no linger, small batches | Lower throughput, more requests |
| Strongest safety | Acks all, idempotence, several replicas | Both slower |
A motorway carries the most cars per hour at a moderate speed and density. Packed too tightly, traffic slows and the journey time for every driver stretches. The road’s best usable capacity is a little below its absolute maximum.
Plot latency against throughput over several runs. The point where the line bends upward, the knee, is the sensible operating limit. Running production right at the maximum leaves no room for bursts.
TThrottling: Fixed Rate Versus Full Speed
The throughput option decides whether the tool pushes as hard as it can or sends at a steady chosen rate.
Setting the throughput target to minus one removes the limit, and the tool sends as fast as the producer and cluster allow. This finds the maximum throughput. Setting a positive target, for example 50,000 messages per second, makes the tool pace itself to that rate. That measures latency at a given load, which is how real applications behave: they send at the rate their users create, not as fast as possible.
What is the ceiling?
Use no limit. Watch throughput, and expect latency to be poor since the system is pushed to saturation.
How fast is it at our real load?
Use a fixed rate at or near your expected traffic. Latency here reflects normal operation.
Where does it break?
Run a series of fixed rates, stepping up each time, and note where latency turns bad.
Can it handle a burst?
Alternate between low and high rates using separate runs, and watch recovery.
Find the ceiling with no limit
Say it reaches about 300,000 messages a second.
Step through fixed rates
Run at 25, 50, 75 and 90 percent of that ceiling, each for the same duration.
Record latency percentiles at each step
Watch which step first breaks your latency goal.
Choose the safe operating point
Pick a rate with headroom below the step that broke the goal.
If you ask for 100,000 a second but the system can only deliver 80,000, the tool reports about 80,000. Always compare the achieved rate with the target. A gap means you have found the ceiling, not a working configuration.
CCoordinated Omission: When Latency Lies
A subtle measurement trap makes systems look faster than they are under stress.
Imagine a shop where customers arrive every minute, but the cashier sometimes freezes for ten minutes. A naive stopwatch measures each customer’s service time from the moment the cashier starts on them. The frozen period barely shows up, because the queue of customers who arrived during the freeze is never timed from their arrival. This hides the real delay users felt. The trap is called coordinated omission: the measurement slows down along with the system, so the worst moments are under-counted.
A load generator that sends the next message only after the previous one is handled, or that pauses when the system is slow, can fall into this. Kafka’s producer is asynchronous and buffers messages, so the situation is less severe than with a simple one-at-a-time client. Still, if the producer’s buffer fills and the send call blocks, time spent blocked may not be counted in the message’s measured latency. The result is that overload can look milder than it is.
A doctor measures wait times only from when each patient enters the consulting room, not from when they arrived in the waiting room. The clinic looks efficient, while patients sit for hours outside.
Ways to reduce the risk
- Use fixed-rate runs so the offered load does not slow down with the system
- Watch producer metrics such as time blocked and buffer usage
- Compare achieved throughput with target throughput
- Repeat with an end-to-end timing that stamps messages at creation
Signs you may be fooled
- Great latency numbers but throughput far below the target
- Producer buffer nearly full all the time
- Long, unexplained pauses in the interval lines
- Maximum latency much smaller than the visible pauses
Treat latency numbers from a saturated run as optimistic. Trust latency numbers from a run where the achieved rate equals the target and the buffers stay mostly empty.
RRealistic Payloads and the Compression Trap
What you send changes how fast it goes, especially when compression is on.
The tool can generate messages of a given size from generated data, or read sample messages from a file you provide. Generated data is often simple and repetitive, so it compresses far better than real business data such as JSON with many different values. If you enable compression and test with easy data, the numbers will look wonderful and mean little.
| Payload choice | Advantage | Risk |
|---|---|---|
| Generated data of a chosen size | Easy, and exact control over size | May compress unrealistically well; ignores real structure |
| Sample file of real-looking messages | Realistic size mix and compressibility | Must be free of real personal data; the tool picks samples, so make the file representative |
| Fixed size for every message | Clean comparisons | Real traffic usually mixes sizes |
| Mixed sizes via a file | Closer to reality | Harder to reason about single numbers |
Message size sets the limit
Small messages are limited by processing per message. Large messages are limited by bytes per second, meaning network and disk.
Compressibility
Repetitive data shrinks a lot. Encrypted or already compressed data shrinks hardly at all.
Key distribution
Keys decide the partition. A few very common keys create hot partitions that the tool’s default test may never show.
Size variety
Test small, typical and large sizes, since each stresses a different resource.
Testing a luggage conveyor with empty boxes says little about airport day, when suitcases are heavy and oddly shaped. Pack the test bags like real ones.
Payload files are copied around, logged and left on disks. Use synthetic data with realistic shape, not real records.
WWarm-up and Steady State
The first seconds of a run are not typical. Measure the part that is.
At the start, several slow things happen at once. The producer must find the cluster, open connections and load metadata. The Java runtime is still optimising its code as it runs. Broker caches are cold. Operating-system memory for the files is not yet filled. Results from this period are often worse than the steady rate that follows. A run that is too short measures mostly the warm-up.
Warm-up
Connections open, code is optimised, caches fill. Numbers are unstable.
Steady state
Throughput and latency settle. This is the part to measure and report.
Cool-down or drift
In long runs, background work such as log rolling, compaction or disk cleaning can cause slow drifts or dips. Note them, since they are part of real life.
Run a short warm-up first
Send a small batch of messages before the real run, and discard its results.
Read the interval lines
Look at the per-interval numbers, not just the final summary, to see where the steady part begins.
Run long enough
Several minutes at least for throughput, and much longer to see disk-related effects.
Stop before the data volume distorts
Very long runs fill disks and trigger retention deletion, which changes what you measure.
If you expect about 100,000 messages a second and want a five-minute steady window, you need on the order of thirty million messages, not a hundred thousand. A run that finishes in one second measures start-up, not Kafka.
An athlete’s first kilometre is slower while muscles warm up. A race time based on the first minute would misjudge them badly.
EThe Environment: Machines, Network and Disks
The same command on two setups gives two answers. Describe the setup fully.
| Part | Why it matters | What to record |
|---|---|---|
| Broker machines | Processor, memory and disk set the broker ceiling | Type, cores, memory, disk type and count |
| Disks | Every message is written to disk, and replicas write too | Kind of storage, throughput limits, and whether it is shared |
| Network | Replication multiplies traffic between brokers | Link speed, distance between machines and zones |
| Client machine | The load generator can itself be the bottleneck | Cores, memory, network, and other work running on it |
| Java runtime and settings | Pauses for memory clean-up affect latency | Version and memory settings on client and brokers |
| Kafka version and configuration | Behaviour and defaults change between releases | Exact versions, and all non-default settings |
| Other tenants | Shared machines and shared networks add noise | Whether the cluster and network were otherwise quiet |
A useful sanity check is to do the arithmetic. If one producer sends 200 megabytes a second to a topic with three copies, the cluster receives 200 from the producer and moves 400 more between brokers for the followers, and writes 600 megabytes a second to disks in total. If the network link or disks in your setup cannot carry that, the benchmark will find that limit long before Kafka itself is the bottleneck.
A race time depends on the track, weather, shoes and the runner. A record without those details cannot be compared with another record.
Local tests are useful for checking the method and comparing settings, but their absolute numbers say little about a real cluster with separate machines, real disks and a real network.
DDesigning a Fair Experiment
Good benchmarks are small experiments with a question, a hypothesis and controlled conditions.
Write the question
For example: does turning on compression raise maximum throughput for our typical JSON messages, and what does it do to the 99th percentile latency?
State a guess
Compression should raise throughput because the network is the limit, at a small latency cost. A guess makes surprises visible.
Change one thing at a time
Keep every other setting identical between runs. If two things change, you cannot say which caused the effect.
Establish a baseline
Run the unchanged configuration first, several times, so you know the normal variation.
Repeat each run
Run each variant at least three to five times and look at the spread.
Record everything
Write down versions, settings, the exact command, the environment and the time of the run.
Decide from the evidence
Ask whether the difference is larger than the run-to-run noise. If not, there is no proven difference.
To learn whether a new fertiliser helps, gardeners compare two identical plots in the same weather, changing only the fertiliser. If they also changed the seed and the watering, the result would prove nothing.
If three identical runs give 210, 225 and 240 thousand messages a second, differences smaller than about 15 percent between variants are inside the noise. Do not celebrate a 5 percent gain that your own repeated baseline can produce by chance.
MBuilding a Test Matrix
A matrix lists the combinations you will try, so the work stays organised and complete.
| Run | Message size | Acks | Batch and linger | Compression | Purpose |
|---|---|---|---|---|---|
| A1 | 1 KB | All | Defaults | None | Baseline |
| A2 | 1 KB | All | Larger batch, 5 ms linger | None | Effect of batching |
| A3 | 1 KB | All | Larger batch, 5 ms linger | Fast codec | Effect of compression |
| B1 | 100 bytes | All | Larger batch, 5 ms linger | None | Small message behaviour |
| B2 | 100 KB | All | Larger batch, 5 ms linger | None | Large message behaviour |
| C1 | 1 KB | One | Larger batch, 5 ms linger | None | Cost of stronger safety, by comparison with A2 |
Notice how each row differs from a neighbour in one way, so comparisons stay clean. A full grid of every combination would explode in size. Start with the questions that matter most, and add rows only when a result raises a new question.
Message size
Small, typical, large.
Durability
Acks levels and idempotence.
Batching and compression
Batch size, linger, codec.
Topic shape
Partitions and replication factor.
Load
Unlimited, and several fixed rates.
Parallel producers
One process, then several.
If you run all baselines in the morning and all variants in the afternoon, time-of-day noise, such as other tenants on a shared cloud, gets mixed into the comparison. Alternate baseline and variant runs to spread that noise evenly.
FFinding the Bottleneck
A result is a symptom. The useful question is which part ran out first.
At the ceiling, one resource is fully busy. Finding it tells you what to change. Improving anything else is wasted effort. The method is to watch all the main resources during the test and see which one is pinned.
flowchart TD
S["Throughput stops rising"] --> Q1{"Client CPU near 100 percent?"}
Q1 -- "Yes" --> C1["Client is the limit: add producer processes or machines"]
Q1 -- "No" --> Q2{"Network link near its limit?"}
Q2 -- "Yes" --> C2["Network is the limit: compress, or use faster links"]
Q2 -- "No" --> Q3{"Broker CPU near 100 percent?"}
Q3 -- "Yes" --> C3["Broker processing is the limit: more brokers or threads"]
Q3 -- "No" --> Q4{"Disk busy or slow?"}
Q4 -- "Yes" --> C4["Disk is the limit: faster storage, more disks or brokers"]
Q4 -- "No" --> C5["Check partitions, settings, replication and pauses"]
| Symptom | Likely cause | Try |
|---|---|---|
| Client processor is pinned, brokers idle | Load generator is too small | Run several tool instances on different machines |
| Network at its limit on the client or broker | Bandwidth ceiling | Enable compression, place machines better, or upgrade links |
| Disk busy and latency rising with load | Storage is the ceiling | Faster disks, more brokers, or fewer replicas if safety allows |
| Everything idle yet throughput is low | Too few partitions, small batches or few in-flight requests | Raise partitions, batch size and linger |
| Regular latency spikes | Memory clean-up pauses, log rolling, or replication hiccups | Inspect runtime and broker logs at the time of the spikes |
A traffic jam on a route could be caused by a narrow bridge, a broken light or a busy junction. Widening the wrong road changes nothing. Find the narrow point first.
WA Worked Example With Illustrative Numbers
Let us walk through a small study from question to conclusion. The numbers are made up to show the method, not to describe any real cluster.
The question
Our service sends 1 KB JSON events at up to 60,000 a second. Can a three-broker cluster with three replicas and full acknowledgement handle it with a 99th percentile send latency under 40 milliseconds?
The setup
Three brokers with fast disks, a twelve-partition topic with three replicas, acks all, one producer machine.
Find the ceiling
Unlimited run, five minutes steady: about 140,000 messages a second, about 140 megabytes a second. The producer machine’s network is at roughly 60 percent, brokers’ processors at 70 percent.
Step through fixed rates
Runs at 30,000, 60,000, 90,000 and 120,000 a second, each five minutes.
Compare latency
99th percentile is 12, 18, 35 and 140 milliseconds respectively. The knee is between 90,000 and 120,000.
Conclude
The target of 60,000 a second is met with about 2 times headroom to the knee, and the latency goal is met. Repeats varied by about 8 percent, so the conclusion is firm.
| Offered rate | Achieved rate | p50 latency | p99 latency | Reading |
|---|---|---|---|---|
| 30,000 per second | 30,000 | 4 ms | 12 ms | Comfortable |
| 60,000 per second | 60,000 | 6 ms | 18 ms | Target load, healthy |
| 90,000 per second | 90,000 | 9 ms | 35 ms | Near the goal limit |
| 120,000 per second | 118,000 | 25 ms | 140 ms | Past the knee, achieved below target |
The honest write-up says: for this hardware, this message size and this setting, at this load, the goal is met with headroom. It does not claim the result for other message sizes, other clusters or other settings.
PClassic Pitfalls and How to Avoid Them
Most bad benchmarks fail in the same handful of ways.
What happens
A run of a few seconds measures start-up, connection set-up and code warming, not steady behaviour.
Better
Size the number of messages for several minutes of steady sending, after a discarded warm-up.
What happens
One small client machine runs out of processor or network before Kafka does, and the team concludes that Kafka is slow.
Better
Watch client resources. If they are busy, add more producer processes on more machines.
What happens
Testing with acks at zero or a single replica gives large numbers that production settings can never match.
Better
Test with the durability settings you will run in production, and show the fast, unsafe result only as a labelled reference.
What happens
Repetitive generated data compresses unrealistically well and hides real network and disk costs.
Better
Use a synthetic sample file with realistic size and variety.
What happens
A single lucky or unlucky run is treated as the truth.
Better
Repeat runs, report the range, and compare differences against the noise.
What happens
Leftover data, other jobs, and unfinished replication from a previous run alter the next one.
Better
Use a fresh topic per series, wait for replicas to be in sync, and keep the cluster otherwise quiet.
Before every series: cluster healthy, replicas in sync, topic created deliberately, client machine idle, settings printed and saved. Ten minutes of checking saves days of arguing about odd results.
IIdempotence and Transactions in the Benchmark
Two correctness features change performance and deserve their own test rows.
An idempotent producer makes retries safe, so a message resent after a timeout is not stored twice. The broker tracks sequence numbers per producer and partition to detect duplicates. This adds a little bookkeeping and puts some limits on other settings, but the cost is often small compared with the safety it gives. In recent Kafka versions it is enabled by default, though you should confirm this for your version.
A transactional producer groups several sends into an all-or-nothing unit, used for exactly-once processing across topics. The tool can be configured with a transactional identifier and a transaction duration, so that it commits after a chosen number of milliseconds. Transactions add extra round trips and coordination, so throughput and latency are affected, and the effect depends strongly on how long each transaction lasts. Short transactions pay a fixed cost often. Long transactions pay it rarely but hold messages back from readers longer.
| Mode | What it protects | Benchmark effect |
|---|---|---|
| No idempotence | Nothing against duplicate retries | Fastest, but duplicates possible on failures |
| Idempotent producer | No duplicates from retries within one producer session | Small overhead |
| Transactions with short commits | Atomic groups of messages | Frequent commit cost, lower throughput |
| Transactions with long commits | Atomic groups, fewer commits | Better throughput, but readers wait longer for committed data |
Sending each parcel with a tracking number stops the depot from accepting a duplicate. Sending a whole crate that either arrives complete or not at all takes more paperwork per crate, so bigger crates mean less paperwork per parcel but a longer wait before anything in the crate is usable.
A transactional benchmark only tells the full story with consumers that read committed data. Send-side numbers alone will look better than the delay a real reader experiences.
SSecurity Settings and Test Safety
Encryption and login cost performance, and test tools can create risk if handled carelessly.
Encryption in transit
TLS uses processor time on clients and brokers, and can reduce maximum throughput noticeably, especially for small messages. Test with TLS on if production uses it.
Authentication and authorisation
Login checks add work when connections open, and permission checks add small costs per request. Test with the same mechanisms production will use.
Credentials in commands
Passwords typed into command lines are visible in shell history and process lists. Use config files with strict permissions, or a secret store.
Test traffic in shared clusters
A full-speed run can starve other teams of disk and network. Benchmark in an isolated cluster, or agree windows and limits first.
Real data in test files
Use synthetic payloads only. Delete test topics and files afterwards.
Left-over permissions
Remove temporary access rules and test accounts when the study ends.
The tool is designed to push as hard as it can. Against a live cluster it can slow or destabilise real services. If production testing is unavoidable, use a strict throttle, a dedicated topic, quotas for the test identity, and a clear stop plan.
Testing how many people a fire exit can handle is done with a drill on a day the building is empty, not by filling the lobby during business hours.
MMonitoring While the Test Runs
The tool’s summary tells you what happened. Monitoring tells you why.
| Where | What to watch | What it tells you |
|---|---|---|
| Client machine | Processor, memory, network use, garbage-collection pauses | Whether the load generator is the limit |
| Producer metrics (via the print-metrics option or a metrics system) | Batch size average, records per request, request latency, buffer availability, time blocked, compression ratio, retries | How batching, compression and the buffer behave |
| Broker machines | Processor, disk throughput and wait times, network use, memory | Which broker resource is saturated |
| Broker metrics | Request handler idle time, request queue time, produce request time breakdown, under-replicated partitions | Where time is spent inside the broker, and whether replication keeps up |
| Replication | Follower lag and in-sync replica changes | Whether followers are falling behind under load |
| Logs | Warnings about slow requests, leader changes, disk problems | Explanations for spikes |
Average batch size
If far below the configured batch size, batches are being sent early. Raising linger may help.
Buffer available and blocked time
Falling free space or rising blocked time means the producer cannot send fast enough.
Request handler idle percentage
Low idle means brokers are short of processing threads or processor time.
Under-replicated partitions
Above zero means followers are behind, which can slow acknowledgement with acks all.
Record start and end times of each run, and use synchronised clocks on all machines. Then a spike in the interval output can be matched to a broker event in the logs.
Names and availability of client and broker metrics have changed over releases. Confirm them in the documentation for your version before building dashboards or alerts.
SScaling the Load Generator
One tool process can only push so hard. To test bigger clusters you need more load.
A single producer process has limits: its own threads, its network link, and the partitions it happens to be writing to. Real systems have many producer applications at once. To model that, run several copies of the tool in parallel, on separate machines if needed, and add up their results. Give each copy its own share of the target rate.
Start with one process
Record its ceiling and how much of the client machine it uses.
Add processes gradually
Two, four, eight, each on a separate machine when the client’s own resources become busy.
Start them together
Use a scheduler or a script so all processes begin and end at the same moment, otherwise the overlap window is short.
Combine results correctly
Add throughputs. Do not average percentiles across processes. Look at each one, or merge the raw latency data if you have it.
To test a stadium’s exits you need thousands of people leaving at once, not one very fast walker. And you count the total who left, not the average walking speed.
The 99th percentile of the combined traffic is not the average of each process’s 99th percentile. Averaging hides tails. Compare processes side by side, or collect all latency measurements and compute percentiles once.
BBeyond the Producer: Consumers and End-to-End Tests
The producer test is half of a full picture.
Kafka also ships a consumer performance tool that reads messages and reports how fast it does so. It is used the same way in spirit: choose a topic and a number of messages, and read the throughput. There are also end-to-end approaches, in which messages carry a creation timestamp and the consumer subtracts it from the arrival time, giving the delay from creation to reading. That is what a user of the data really feels.
| Test | Question answered | Who is stressed |
|---|---|---|
| Producer performance | How fast can we write, and how long until acknowledgement? | Producer, network, broker write path, replication |
| Consumer performance | How fast can we read? | Broker read path, network, consumer |
| End-to-end latency | How long from creation to being read? | Everything, including consumer polling behaviour |
| Mixed load | How do reads and writes affect each other? | Brokers sharing disks, cache and network |
Real clusters write and read at once. Reads served from memory are cheap, but readers that fall behind cause disk reads that compete with writes. A producer-only test can flatter the cluster.
A postal service measures how fast parcels are accepted at the counter, how fast they are delivered from the depot, and how long a parcel takes door to door. Each number is valuable and none replaces the others.
CFrom Results to Capacity Planning
The goal of most benchmarks is a decision: how much do we need?
Forecast the load
Estimate peak messages per second and message size, now and for the next year or two. Include growth and seasonal peaks.
Convert to bytes
Multiply rate by size, then by the replication factor to get total cluster write load.
Use the measured safe rate
Take the rate per broker at the knee, not at the maximum, from your tests.
Add headroom
Allow for bursts, broker failure and rebalancing. A cluster that is comfortable with all brokers up must still cope with one down.
Plan storage separately
Disk space depends on retention time, not just speed: bytes per second times seconds retained times replication.
Suppose tests show that each broker safely handles about 60 megabytes a second of incoming data at the knee with three copies. For 50 megabytes a second of peak load, three brokers would run near their safe limit with no failure allowance. Adding a fourth or fifth broker gives room to survive a broker failure and to grow. The exact numbers will differ for you, but the steps are the same.
A restaurant does not staff for average Tuesday lunch. It staffs for Saturday night with a cook off sick.
When a broker fails, the others take over its partitions and its traffic. Test performance during such an event too, because that is the moment when headroom is used.
RReporting Results Honestly
A result without its conditions cannot be trusted or reproduced.
| Section | What to include |
|---|---|
| Question | What the study set out to answer, and the target or goal |
| Environment | Kafka version, machine types, disks, network, client details, region or zone layout |
| Configuration | Topic settings, producer settings, broker non-default settings, security in use |
| Method | Message size and content, run duration, warm-up, number of repeats, load pattern |
| Results | Throughput and latency percentiles per run, with the range across repeats |
| Observations | Which resource was the bottleneck, and any events seen during the run |
| Conclusion and limits | What was decided, and what the results do not show |
Honest reporting
- States the conditions along with every number
- Shows ranges, not a single best run
- Includes runs that surprised or disappointed
- Names its limits and unknowns
Misleading reporting
- Quotes the peak from one lucky run
- Leaves out safety settings
- Compares numbers from different setups
- Claims results for workloads never tested
Save the full tool output, the exact commands and the monitoring graphs for every run. Weeks later, someone will ask how a number was produced, and the raw files are the only reliable answer.
AAutomating and Catching Regressions
A benchmark you run once helps once. A benchmark you can rerun helps every release.
Because the tool is a command-line program, it fits easily into scripts and automated pipelines. A short, standard benchmark can run after each significant change: a new Kafka version, a configuration change, a new instance type, or a new network policy. Comparing each result against a stored baseline reveals slowdowns early, when the cause is still easy to find.
Create an isolated, repeatable environment
Build the test cluster from code so every run starts from the same shape.
Script the run
Create topic, warm up, run the standard cases, collect output and metrics, then clean up.
Store results with metadata
Keep versions, settings and timestamps beside the numbers, in a place where trends can be plotted.
Compare against a baseline with tolerance
Flag a change only when it exceeds the known noise, for example ten percent worse than the median of recent runs.
Investigate flagged runs
Repeat the run, and if the change is real, bisect between the last good and first bad versions.
Small and fast standard suite
A few well-chosen cases that finish in minutes, run often.
Longer suite on a schedule
A deeper test nightly or weekly, including durability and failure scenarios.
Flaky thresholds
Alerts that fire on normal noise teach the team to ignore them.
Shared, noisy runners
Cloud machines with unpredictable neighbours make small changes impossible to see.
A slow drift of two percent per release is invisible in any single comparison but obvious on a chart over a year.
UReal-World Uses
Teams reach for producer benchmarks in a few recurring situations.
Sizing a new cluster
Before buying or provisioning brokers, teams measure per-broker limits with their own message sizes and replication settings, then divide forecast load by the safe per-broker rate.
Choosing between instance or disk types
The same test on two setups shows whether a costlier disk or faster network actually raises the ceiling, or whether the bottleneck is elsewhere.
Validating a version upgrade
Running a standard suite before and after an upgrade shows whether throughput or latency changed, before real traffic is affected.
Tuning a busy producer application
Teams test batch size, linger and compression settings against representative payloads, then apply the winner to the real application.
Checking the cost of security
Measuring the same load with and without encryption gives an evidence-based figure for the extra hardware needed.
Proving a service level
Showing that a latency goal holds at forecast peak load, with headroom, supports launch decisions.
Many organisations have described their Kafka testing in talks and articles, but practices vary and change. Treat the uses above as common patterns, not as a description of any one company’s method.
BBatching Deep Dive: Size, Linger and Request Shape
Batching is the single biggest lever on producer throughput, so it deserves a closer look.
The producer keeps one open batch per partition it is writing to. A batch is sent when it is full, when its linger time runs out, or when the producer is closing or flushing. Several ready batches for the same broker are grouped into one request. So the size of what actually travels depends on three things: how fast messages arrive, how many partitions they spread over, and the two limits, batch size and linger time.
Batches fill quickly
When messages arrive faster than the network can send, batches fill to their maximum before the linger time matters. Bigger limits allow bigger batches.
Linger decides
When messages arrive slowly, batches leave when linger expires, often nearly empty. A small linger adds latency for little gain.
Batches are split thinner
The same message rate spread over many partitions makes each batch smaller, so more requests carry less each.
Key patterns matter
Random keys spread messages evenly. A few hot keys fill a few batches and leave others idle.
flowchart TD
M["Message arrives"] --> B{"Open batch for its partition?"}
B -- "No" --> N["Start new batch and linger timer"]
B -- "Yes" --> A["Add to batch"]
N --> A
A --> F{"Batch full?"}
F -- "Yes" --> S["Ready to send"]
F -- "No" --> T{"Linger time reached?"}
T -- "Yes" --> S
T -- "No" --> W["Wait for more messages"]
S --> R["Grouped with other ready batches for the same broker"]
Worked numbers
Suppose messages are 1 KB and the batch limit is 16 KB. A full batch holds about sixteen messages. If 60,000 messages a second are spread over twelve partitions, each partition receives about 5,000 a second, so a batch could fill in about three milliseconds. A linger of five milliseconds is then long enough for nearly full batches. If the same rate spreads over 240 partitions, each gets about 250 a second, a batch would need sixty-four milliseconds to fill, and linger will send small batches long before that. The lesson: the right settings depend on the arrival rate per partition, not just the total.
A minibus leaves when all seats are taken or after five minutes, whichever comes first. On a busy route it fills instantly. On a quiet route it always leaves after five minutes with a few passengers. Adding more routes for the same passengers makes every bus emptier.
The producer’s average batch size tells you directly whether batching is working. If it is a small fraction of your limit, raise linger a little, or reduce the number of partitions each producer writes to.
CCompression Deep Dive
Compression trades processor time for smaller data. Whether it wins depends on which resource is scarce.
The producer compresses each whole batch before sending, so bigger batches usually compress better, since there are more repeated patterns inside them. Brokers typically store the batch in its compressed form and pass it on to consumers, who decompress it. So a good compression ratio saves network from producer to broker, disk space, replication traffic and network to consumers, at the cost of processor time on the producer and on consumers.
| Codec family | Character | Typical fit |
|---|---|---|
| None | No processor cost, full size | Data that does not compress, or a network with plenty of room |
| Fast, light codecs | Modest shrinking with low processor cost | Most busy pipelines that want a cheap win |
| Balanced codecs | Better shrinking, moderate processor cost | When network or disk is the limit and processors have room |
| Heavy codecs | Strong shrinking, high processor cost | Storage-heavy or bandwidth-starved cases with spare processing |
Use realistic data
Compression results only mean something on data shaped like yours.
Test each codec on the same setup
Hold everything else fixed and compare throughput, latency, processor use and the compression ratio.
Watch both ends
Check producer processor use, broker processor use and, if possible, consumer processor use.
Decide by the bottleneck
If the network was the limit and processors have room, compression often raises the ceiling. If processors were already the limit, it may lower it.
If the topic is configured to use a different compression from what the producer used, brokers may need to decompress and recompress data, which costs broker processing. Keep the choices consistent unless you have a reason.
Vacuum-packing clothes takes effort at home but lets more fit in the suitcase and on the lorry. It only pays off if the lorry space is the scarce thing and you have the time to pack.
PPartitions and Parallelism
Partitions are how Kafka spreads work, and how a benchmark exposes whether the spread is enough.
Each partition is written by its leader, and a leader handles its partitions using shared broker threads. Too few partitions and some brokers or disks sit idle. Too many and each carries less traffic, batches become thin, and the cluster spends more effort on bookkeeping and replication between partitions. There is a sweet spot, and a benchmark is the way to find it.
| Partition count | Likely effect on throughput | Likely effect on other things |
|---|---|---|
| Fewer than brokers | Some brokers not used | Uneven load, lower ceiling |
| A few times the number of brokers | Good spread, thick batches | Often a good balance |
| Very many | Thin batches, more requests | More memory, more metadata, slower recovery after failures |
Leader balance
Check that leaders are spread evenly. If one broker leads most partitions, it carries most of the write load, and the benchmark finds that one broker’s limit instead of the cluster’s. Look at the distribution before you start, and rebalance leaders if needed.
Leaders per broker
Should be roughly equal across brokers.
Partitions per disk
Where brokers have several disks, ensure data is spread evenly across them.
Key distribution
If keys are skewed, some partitions get most of the traffic.
Rack or zone spread
Replicas placed across zones add network hops that affect latency.
A supermarket with twelve checkouts but one busy cashier’s queue is no faster than a shop with one till. Spreading customers evenly is the whole point of opening more checkouts.
Run the same test at several partition counts, such as one, three, twelve and thirty-six, and plot the ceiling. The curve rises, flattens and may fall. Pick the point at the start of the flat part.
FFailure and Recovery Testing
Performance on a good day is only half of the story. What happens when something breaks?
Real clusters lose brokers, suffer slow disks and drop network packets. A benchmark that only runs in perfect health will not reveal how your setup behaves in trouble. Combine a steady, throttled load with a planned failure and watch what the producer sees.
Start a steady fixed-rate load
Choose a realistic rate with comfortable headroom, and let it reach steady state.
Stop one broker cleanly
Leadership for its partitions moves to other brokers. Watch the latency spike and how long it lasts.
Observe the producer
Look for retries, timeouts, errors and any message loss or duplicates, according to your safety settings.
Restart the broker
Watch it catch up. Catch-up traffic competes with normal load and can slow producers.
Repeat with an abrupt failure
Killing a broker without a clean stop is harsher, and detection takes longer.
| Scenario | What you learn |
|---|---|
| Clean broker shutdown | Best-case failover time and latency blip |
| Abrupt broker loss | Detection delay and worst-case producer errors |
| Broker restart under load | Recovery traffic effects |
| Slow disk or network on one broker | Whether one weak broker drags down acks all writes |
| Fewer in-sync replicas than required | Whether producers fail fast or wait, given your minimum in-sync setting |
Failure tests intentionally break things. Run them in an isolated environment with agreed access, and never as a surprise on a shared or production system.
A fire drill tests more than how fast people walk on a normal day. It reveals whether doors open, whether signs are clear and how long it takes when a corridor is blocked.
DDesign Patterns and Anti-Patterns
A few working habits recur in good benchmarks, and a few recur in bad ones.
Patterns that work
Question first
Every study starts with a written question and a success criterion.
One variable at a time
Change a single thing between paired runs.
Baseline and repeats
Run the baseline several times to learn the noise.
Step-load ladder
Increase fixed rates in steps and find the knee.
Monitor everything
Client, network, brokers, disks and logs, all recorded with run times.
Infrastructure as code
The test cluster is built from scripts so it is identical each time.
Anti-patterns to avoid
Chasing the biggest number
Tuning until a headline throughput appears, using settings no production system could use.
Better
Target a stated goal under production-like conditions.
Changing many settings at once
Adjusting batch size, compression, acks and partitions together, then not knowing what helped.
Better
Change one setting per comparison.
Benchmarking a live shared cluster
Running full-speed tests where real services depend on the same brokers.
Better
Use an isolated cluster, or strict limits and agreed windows.
Trusting the average
Deciding on average latency while the tail is poor.
Better
Decide on percentiles that match user impact.
Undocumented runs
Numbers with no record of settings or environment.
Better
Save configuration, commands and raw output with every result.
BBest Practices and Common Mistakes
A checklist to keep by your side.
Best practices
- Write the question and the target before running anything
- Use production-like message sizes, content and durability settings
- Create the topic deliberately with the intended partitions and replicas
- Warm up, then measure a long steady window
- Use fixed-rate runs to measure latency, and unlimited runs to find ceilings
- Repeat runs and compare differences with the noise
- Watch client, network, broker and disk resources together
- Look at percentiles, not only averages
- Record configuration and keep raw outputs
- Report results with their conditions and limits
Common mistakes
- Runs so short they measure start-up
- A weak client machine that becomes the bottleneck
- Fast but unsafe settings that production will not use
- Compressible fake data that flatters compression
- Comparing runs from different environments
- Reading latency from a saturated run as if it were normal
- Averaging percentiles across processes
- Leaving old data and unfinished replication in place
- Testing on a shared production cluster
- Quoting one best run as the result
What was the question? What were the conditions? How many repeats? Which resource was the bottleneck? What is the noise? If a number arrives without answers to these, do not build a decision on it.
MMyths and Misunderstandings
Some beliefs sound sensible and lead teams astray.
| Myth | Reality |
|---|---|
| “The tool tells us how fast Kafka is.” | It tells you how fast this producer setup ran against this cluster under these conditions. |
| “Bigger batches are always better.” | They raise throughput up to a point and add latency. Very thin traffic per partition cannot fill them. |
| “More partitions always means more speed.” | Beyond a point, batches thin out and overhead grows. |
| “Compression always helps.” | It helps when network or disk is scarce and processors have room, and on data that compresses. |
| “Average latency shows user experience.” | Users feel the tail, which percentiles reveal. |
| “A laptop test predicts production.” | It checks the method, not real capacity. |
| “If throughput hits the target, we are fine.” | Latency, headroom, failure behaviour and safety settings matter too. |
| “The producer test covers the whole pipeline.” | It covers only the send side. Consumers and end-to-end delay need their own tests. |
| “Run it once, it is deterministic.” | Real systems vary. Repeats reveal how much. |
CCloud and Container Cautions
Modern platforms add layers that can silently change your results.
Temporary performance
Some cloud machines and disks give fast performance for a while, then throttle. A short test looks great, a long one collapses. Run long enough to see the sustained rate.
Shared hardware
Other customers’ load on the same host can change your numbers from hour to hour. Repeat and interleave runs.
Container resource limits
Processor and memory limits apply even when the host has more. A limit on the load generator can quietly cap the test.
Overlay networks and zones
Extra network layers and cross-zone traffic add delay and costs. Place clients and brokers as production will be placed.
Network-attached disks
These have provisioned speed limits that differ from local disks. Know your limits and test near them.
Data transfer charges
Large tests can create real bills for traffic between zones. Estimate before running.
| Symptom | Possible platform cause |
|---|---|
| Fast at first, then slower | Burst allowance ended on disk or network |
| Results differ by time of day | Shared hardware or shared network load |
| Client stuck at a rate below expectations | Processor or network limit on the container |
| Latency higher than same-machine tests | Cross-zone or overlay network hops |
A sprinter who can run fast for ten seconds is not a marathon runner. Cloud burst limits are the same: the opening seconds do not show what can be sustained.
PPutting It All Together: A Study Plan
A complete plan from start to finish, in the order to do things.
Define the question and the goal
Peak rate, message size, latency target, durability needs, and the decision the study will support.
Prepare the environment
Isolated cluster of the intended shape, healthy and quiet. A client machine large enough not to be the limit.
Prepare the data
Synthetic payload file with realistic sizes and compressibility.
Create topics deliberately
Correct partitions, replication and minimum in-sync setting. Check leader balance.
Baseline runs
Production settings, several repeats, to learn the noise and the ceiling.
Step-load ladder
Fixed rates in steps. Record percentiles and find the knee.
One-variable comparisons
Batching, compression, partitions, security, transactions, each against the baseline.
Failure tests
Broker stop, abrupt loss and restart under steady load.
Consumer and end-to-end checks
Confirm the whole pipeline meets the goal.
Report and decide
Write up conditions, results, bottlenecks and limits, then make the sizing or tuning decision, and save the scripts for reruns.
Even a two-hour study with a baseline, a step ladder and one comparison gives more trustworthy knowledge than dozens of casual runs. Grow the plan as questions arise.
SSimple Statistics for Comparing Runs
You do not need advanced mathematics to avoid fooling yourself. A few simple habits are enough.
Suppose the baseline gives 200, 210 and 205 thousand messages a second, and a variant gives 215, 220 and 212. The variant’s average is about 216 and the baseline’s about 205. Is the variant better? The spread inside each group is about 10 thousand. The gap between groups is about 11 thousand. That is close to the noise, so the honest answer is “probably slightly better, but run more repeats”. If the variant had given 250, 255 and 248, the gap would be far larger than the spread and you could be confident.
| Habit | How to do it | Why |
|---|---|---|
| Use the median for a typical value | Sort the repeats and take the middle one | One odd run does not distort it |
| Report the range | Show the lowest and highest repeat | Shows how much the result wobbles |
| Compare the gap to the spread | Is the difference bigger than the range within a group? | Separates real effects from noise |
| Look at latency percentiles per run | Do not average percentiles from different runs blindly | Tails hide in averages |
| Add repeats when unsure | Run a few more of each variant | More data narrows doubt |
| Check for trends across the order of runs | Plot results by the time they ran | Reveals warming, throttling or time-of-day effects |
If two runners’ finishing times differ by one second but each runner’s own times vary by three seconds from race to race, you cannot say one is faster. You need more races.
Decide in advance: “we adopt the change if the median throughput rises by at least 10 percent and the 99th percentile does not worsen.” Setting the rule first stops you from reading the results in whichever way you prefer.
BThe Producer Buffer and Back-Pressure
When the producer produces faster than the cluster accepts, memory fills, and behaviour changes.
The producer keeps unsent messages in a pool of memory. Messages enter the pool when the application sends them and leave when the broker acknowledges them. If messages arrive faster than they leave, the pool fills. Then the producer applies back-pressure: the send call waits for space, up to a configured maximum blocking time, and then fails. This is a healthy safety valve, but it changes what a benchmark measures.
flowchart LR A["Application sends"] --> P["Buffer pool"] P --> S["Sender thread"] S --> B["Broker"] B -->|"acknowledgement frees space"| P P -->|"pool full"| W["Send call waits, then may fail"]
Load below capacity
The buffer stays mostly empty. Messages leave almost as soon as they arrive.
Load at capacity
The buffer holds a modest backlog. Latency rises a little.
Load above capacity
The buffer fills. The application waits. Throughput equals capacity, and measured latency depends on how the waiting is counted.
Buffer space near zero
The cluster or network cannot keep up with the producer.
Rising blocked time
The application is waiting on the producer, so you are past the ceiling.
Bigger buffer
Absorbs short bursts, but does not raise the sustained ceiling.
More capacity
Raise the real ceiling with brokers, partitions or network, then test again.
A bath fills if the tap runs faster than the drain. A bigger bath delays the overflow, but only a wider drain solves it.
A big buffer absorbs the whole run without ever blocking, so a short test may look perfect. A longer run then reveals the true limit. Another reason to run for minutes, not seconds.
RRetries, Timeouts and Errors in Results
A run that completes is not always a run that succeeded. Look for hidden trouble.
When a send fails for a temporary reason, such as a leader change, the producer retries automatically within limits set by its timeouts. Retries hide short problems from the application, but they add delay, and they consume capacity. A benchmark that shows good throughput while retries quietly climb is telling you the system is under strain.
| What to check | Where to see it | What it suggests |
|---|---|---|
| Retry rate | Producer metrics | Instability, leader changes, or overloaded brokers |
| Error rate | Tool output and producer metrics | Sends that gave up. Any errors mean the run is not clean. |
| Timeouts | Producer logs | Requests waiting too long for brokers or replicas |
| Records sent versus requested | Tool summary | Confirms every message was acknowledged |
| Under-replicated partitions | Broker metrics | Followers falling behind, which slows acks all |
A clean run
- All requested records acknowledged
- Retries near zero
- No errors or timeouts
- Replicas stay in sync
A suspect run
- Some records failed or timed out
- Retries rising during the run
- Replicas dropping out of sync
- Large pauses in the interval lines
A courier who reports “all parcels delivered” but had to re-attempt half of them has not had a good day. The retries cost time and effort that the final count does not show.
If a run had errors or heavy retries, investigate before using its numbers. Mixing clean and troubled runs makes comparisons meaningless.
IReading the Interval Lines Like a Chart
The lines printed during the run are a time series. Reading them in order tells a story the final summary cannot.
While it runs, the tool prints a line for each reporting interval, giving the count, rate and latency for just that stretch. If you copy those lines into a spreadsheet and plot rate and latency against time, patterns appear at once.
| Shape over time | Likely meaning | Action |
|---|---|---|
| Low at first, then steady | Normal warm-up | Exclude the early part from results |
| Steady, then a sudden drop | Burst allowance ended, a broker issue, or memory clean-up storms | Check platform limits and logs at that time |
| Regular sawtooth | Periodic background work such as log rolling, or batching cycles | Find the period and match it to a broker setting |
| Slow downward drift | Disk filling, caches turning over, or growing files | Run longer and watch disk and memory |
| Rare huge spikes | Leader elections, pauses, or network hiccups | Correlate with broker logs |
Use a modest interval
Around one second is common. Much longer hides short spikes, much shorter produces noisy lines.
Save the raw lines
Keep them with the run. The summary alone cannot be re-analysed.
Overlay with monitoring graphs
Put the tool’s rate line and the broker’s disk and processor graphs on the same time axis.
Mark events
Note the exact time of any change or failure you introduce, so it can be found on the chart.
A doctor reading a single blood pressure number learns less than one reading a chart of the whole day. The pattern over time shows what a snapshot hides.
TA Tuning Playbook: Symptom, Cause, Try
When a benchmark disappoints, work through these ideas in order of cost and likelihood.
Confirm the test is fair
Client not saturated, topic shape right, data realistic, run long enough. Many “slow Kafka” reports end here.
Improve batching
Raise linger a few milliseconds and increase the batch size, and check that average batch size now grows.
Try compression
If the network is the limit and processors have room, test a light codec on realistic data.
Adjust partitions
Ensure enough partitions to use every broker and disk, but not so many that batches thin out.
Check in-flight requests and buffer
Allow enough outstanding requests and buffer memory so the producer never starves the network.
Look at the broker side
Threads, disks, network, replication lag, and leader balance.
Scale out
If everything is tuned and one resource is still full, add capacity: brokers, faster disks or links.
| Symptom | First thing to try | Watch afterwards |
|---|---|---|
| Low throughput, everything idle | More linger and larger batches; more partitions | Average batch size, records per request |
| Good throughput, poor tail latency | Reduce linger, check for pauses, check replication lag | 99th percentile, garbage-collection and broker logs |
| Network saturated | Compression | Compression ratio, processor use |
| Client processor saturated | More producer processes or machines, lighter compression | Client processor and per-process rates |
| Disk saturated on brokers | More brokers or disks, faster storage | Disk wait, request time breakdown |
| Latency spikes during broker events | Review timeouts, in-sync settings and failure handling | Retry counts and leader change logs |
Settings that cost nothing and can be undone, such as linger and batch size, come before buying hardware. And always re-run the baseline after each change, so you know that the change did what you think.
Before rebuilding a slow kitchen, check that the cooks have the ingredients, the orders are batched sensibly, and nobody is standing in the doorway. Only then, buy a bigger oven.
OOrganising the Work: Folders, Names and Notes
Good records turn a pile of runs into evidence anyone can check.
flowchart TD S["Study folder"] --> P["Plan: question, goal, decision rule"] S --> E["Environment notes: versions, machines, network"] S --> R["Runs"] R --> R1["Run folder: command, settings, raw output"] R --> R2["Run folder: monitoring graphs and logs"] S --> A["Analysis: tables, charts, conclusions"]
Meaningful run names
Include date, case code and variant, such as a matrix row label, so a folder explains itself.
A short log per run
Anything unusual: a restart, a noisy neighbour, a mistake in the command.
Record software versions
Kafka, Java, operating system and any tooling, for both clients and brokers.
Write for a stranger
Someone who was not there should be able to repeat the run from your notes.
If a colleague can follow your folder and get similar numbers, your benchmark is a piece of knowledge. If not, it is a story.
A recipe that says “bake until done” cannot be repeated. One with temperature, time and quantities can be cooked by anyone, and criticised fairly if it fails.
QQuick Reference: Starting Points by Goal
Where to begin your first test runs for common goals. These are starting hypotheses to test, never final answers.
| Goal | Starting hypothesis | What to confirm by measuring |
|---|---|---|
| Maximum throughput with safety | Acks all, idempotence on, moderate batch size, a few milliseconds of linger, light compression on realistic data | Average batch size grows, network or disk becomes the bottleneck, tail latency stays acceptable |
| Lowest send latency | Acks all or one according to safety needs, minimal linger, modest batch size, no heavy compression | The 99th percentile at your real rate, and that throughput still meets the target |
| Many small messages | Enough linger to form useful batches, compression to cut request overhead | Records per request and broker processor use |
| Few large messages | Batch size large enough to hold them, careful with buffer memory and message size limits | Megabytes per second against network and disk limits |
| Bursty traffic | Generous buffer, fixed-rate tests at burst peaks | Buffer availability during bursts and recovery afterwards |
| Strict ordering with retries | Idempotence on, limited in-flight requests as allowed by your version | Ordering preserved and throughput cost measured |
Hypothesis, not recipe
Every row is a guess to check with your own data and hardware.
Confirm with metrics
Each hypothesis names a metric that shows whether it worked.
Revisit after upgrades
Defaults and internals change, so old findings may expire.
Keep the safe baseline
Always compare any faster setting against a setting you would actually be allowed to run.
A cookbook suggests a starting temperature, and a good cook adjusts by looking in the oven. These starting points are the cookbook. Your measurements are looking in the oven.
The most valuable findings are the times when a hypothesis was wrong. Record them, since they show where your mental model of the system needs to change.
GGlossary of Terms in Plain Words
A quick reference for the terms used in this guide.
| Term | Plain meaning |
|---|---|
| Producer | A program that sends messages to Kafka |
| Broker | A Kafka server that stores and serves messages |
| Topic and partition | A named channel, and its parallel ordered lanes |
| Leader and replica | The broker handling a partition, and its backup copies |
| Throughput | Work done per second, in messages or megabytes |
| Latency | Time one message takes from send to acknowledgement |
| Percentile | The value below which a stated share of measurements fall |
| Tail latency | The slowest few percent of messages |
| Acks | How many brokers must confirm a send before it counts |
| Batch | A group of messages sent together for one partition |
| Linger | How long the producer waits to fill a batch |
| Idempotent producer | A producer whose retries never create duplicates |
| Transaction | A group of sends that succeed or fail together |
| Saturation | The point where a resource is fully busy |
| Knee | The load level where latency begins to rise steeply |
| Warm-up | The unstable early period before performance settles |
| Steady state | The stable period that should be measured |
| Coordinated omission | A measurement flaw where slowdowns hide their own delay |
| Baseline | The unchanged configuration used for comparison |
| Noise | Natural run-to-run variation |
| Headroom | Spare capacity above expected load |
FFrequently Asked Questions
How many messages should I send in a run?
Enough to last several minutes at steady state. Multiply your expected rate by the seconds you want, for example one hundred thousand a second for five minutes is thirty million messages.
Should I test with an unlimited or a fixed rate?
Both. Unlimited finds the ceiling. Fixed rates show latency at realistic load and reveal the knee.
Why is my throughput far below what others report?
Others probably used different message sizes, safety settings, hardware or partitions. Compare conditions, and check whether your client machine, network or a single busy broker is the limit.
Is the latency the tool reports the end-to-end latency?
No. It is the send latency from handing over a message to receiving the acknowledgement. Consumer delay needs a separate end-to-end measurement.
Which acks setting should I benchmark?
The one you will use in production. If you need durability, that means acknowledgement from all in-sync replicas. Show weaker settings only as labelled references.
How do I know if the load generator is the limit?
Watch its processor and network. If it is nearly full while brokers have room, run more tool processes, on more machines if needed.
Can I trust the 99.9th percentile?
Use it as a hint. It is based on far fewer slow samples and is approximate. Compare it only between runs of similar length and repeat to see its variation.
How many repeats do I need?
Three to five per variant is a good start. More if the results vary widely.
Do I need a payload file?
It is strongly advised when using compression or when message contents matter. Generated data can be unrealistically easy to compress. Always use synthetic content, not real customer data.
Can I run the tool against production?
Avoid it. If you must, use a dedicated topic, a strict throttle, quotas and a clear stop plan, since an unlimited run can harm real services.
Why do results change from day to day on the cloud?
Shared hardware, burst allowances and other tenants change available capacity. Interleave runs, repeat, and use long enough tests to see sustained speed.
Do the option names differ between Kafka versions?
Some options have been added or changed over time, and defaults have shifted too. Check the help output and documentation for your version, and record the exact command you ran.
SSummary and Key Takeaways
The whole method in a few lines.
The kafka-producer-perf-test tool sends messages of a chosen size, at a chosen rate, with chosen producer settings, and reports throughput and latency percentiles. On its own it produces numbers. A method turns those numbers into knowledge: ask a clear question, mimic production in message shape, durability and topic layout, warm up and measure steady state, change one variable at a time, repeat to learn the noise, watch every resource to find the bottleneck, and report results with their conditions. Use unlimited runs to find ceilings, and fixed-rate ladders to find the knee where latency turns bad. Remember that send latency is not end-to-end latency, that saturated runs flatter latency, and that safety settings are performance settings. Done this way, a benchmark supports real decisions about sizing, tuning and upgrades.
Key Takeaways
- A benchmark answers a specific question under stated conditions; a number without conditions is a rumour.
- Test with production-like message sizes, content, topic layout and durability settings.
- Warm up, then measure several minutes of steady state, and repeat each run to learn the noise.
- Use unlimited runs to find the ceiling and fixed-rate steps to find the knee; judge latency by percentiles, not averages.
- Change one variable at a time, and watch client, network, broker and disk resources to find the true bottleneck.
- The tool measures send latency only; test consumers and end-to-end delay separately, and beware coordinated omission.
- Plan capacity from the safe rate with headroom for failures, and report results with their environment and limits.