kafkaclitest
Kafka Client Benchmarks
This repository contains performance and stress benchmarks for various Kafka client implementations across Crystal (Kafkaesque) and Go (Confluent Go, Franz-Go).
Stress Benchmark Configuration
- Message Count: 1,000,000 messages
- Linger MS: 100 ms
- Outlier Mitigation: Running 3 iterations of each test and selecting the median run.
- Warmup: A silent warmup phase of 100,000 messages to prime client runtimes and OS socket buffers.
- Coexistence / Zero Redundant Build: Single Crystal binaries are compiled once (
-Dpreview_mtenabled) and runtime worker threads are controlled dynamically via theCRYSTAL_WORKERSenvironment variable to avoid redundant compilation phases. - Environment Execution Modes:
- Single-Core (Stage 1): Pinned to a single core (
taskset -c 2) withCRYSTAL_WORKERS=1for single-thread targets. - Multi-Core (Stage 2): Scaled across all 8 cores (
taskset -c 0-7) withCRYSTAL_WORKERS=8for multi-threaded targets.
- Single-Core (Stage 1): Pinned to a single core (
Benchmark Results
1. Single-Core Results (Pinned to Core 2)
Producer Scoreboard
| Rank | Client Engine | Execution Time | Throughput | Peak RAM (RSS) |
|---|---|---|---|---|
| #1 | Kafkaesque (Single Thread) | 2.90s | 344,828.0 msg/s | 1,124.18 MB |
| #2 | Franz-Go | 3.86s | 259,067.0 msg/s | 50.11 MB |
| #3 | Kafkaesque (Multithread) | 5.61s | 178,253.0 msg/s | 144.74 MB |
| #4 | Go Confluent | 7.53s | 132,802.0 msg/s | 77.02 MB |
Consumer Scoreboard
| Rank | Client Engine | Execution Time | Throughput | Peak RAM (RSS) |
|---|---|---|---|---|
| #1 | Kafkaesque (Single Thread) | 0.88009s | 1,136,247.6 msg/s | 61.10 MB |
| #2 | Franz-Go | 0.97092s | 1,029,953.1 msg/s | 68.12 MB |
| #3 | Kafkaesque (Multithread) | 1.17004s | 854,669.8 msg/s | 144.62 MB |
| #4 | Go Confluent | 8.12438s | 123,086.4 msg/s | 105.65 MB |
2. Multi-Core Results (Pinned to Cores 0-7)
Producer Scoreboard
| Rank | Client Engine | Execution Time | Throughput | Peak RAM (RSS) |
|---|---|---|---|---|
| #1 | Kafkaesque (Multithread) | 2.86s | 349,650.0 msg/s | 146.87 MB |
| #2 | Kafkaesque (Single Thread) | 2.93s | 341,297.0 msg/s | 1,031.47 MB |
| #3 | Franz-Go | 3.15s | 317,460.0 msg/s | 65.08 MB |
| #4 | Go Confluent | 4.74s | 210,970.0 msg/s | 79.76 MB |
Consumer Scoreboard
| Rank | Client Engine | Execution Time | Throughput | Peak RAM (RSS) |
|---|---|---|---|---|
| #1 | Kafkaesque (Multithread) | 0.34031s | 2,938,481.7 msg/s | 142.29 MB |
| #2 | Franz-Go | 0.80846s | 1,236,923.6 msg/s | 79.04 MB |
| #3 | Kafkaesque (Single Thread) | 0.93175s | 1,073,251.8 msg/s | 68.71 MB |
| #4 | Go Confluent | 8.70249s | 114,909.6 msg/s | 110.93 MB |
Client Configurations
Below are the exact code configuration settings used for each client engine during the benchmarks:
1. Kafkaesque (Crystal)
Example Console Startup Output:
🚀 Kafkaesque Producer configured and ready (PLAINTEXT, port 9097)
--- Verbose Configurations ---
Bootstrap Servers: localhost:9097
Settings:
enable.idempotence: true
acks: all
linger.ms: 100
batch.num.messages: 10000
retries: 5
retry.backoff.ms: 100
compression.type: lz4
------------------------------
🚀 Kafkaesque Consumer started (PLAINTEXT, port 9097)...
--- Verbose Configurations ---
Bootstrap Servers: localhost:9097
Settings:
group.id: bench-kafkaesque-178047...
initial_offset_smallest: true
group.protocol: consumer
------------------------------
Producer Setup
producer_config = Kafkaesque::Producer::Config.new(
bootstrap_servers: ["localhost:9097"],
compression_type: "lz4",
settings: {
"enable.idempotence" => "true",
"acks" => "all",
"linger.ms" => "100",
"batch.num.messages" => "10000",
"retries" => "5",
"retry.backoff.ms" => "100",
}
)
Consumer Setup
config = Kafkaesque::Consumer::Config.new(
bootstrap_servers: ["localhost:9097"],
group_id: "bench-kafkaesque-[timestamp]",
initial_offset_smallest: true
)
# Note: Defaults to auto-commit enabled and uses "group.protocol": "consumer" (KIP-848 protocol)
2. Go Confluent (Go)
Example Console Startup Output:
🚀 Go Confluent Producer configured and ready (PLAINTEXT, port 9097)
Settings: enable.idempotence=true, acks=all, linger.ms=100, batch.num.messages=10000, compression=lz4, go.delivery.reports=false
🚀 Go Confluent Consumer started (PLAINTEXT, port 9097)...
Settings: group.id=bench-go-confluent-178047..., auto.offset.reset=smallest, enable.auto.commit=true, group.protocol=consumer, fetch.wait.max.ms=5
Producer Setup
p, err := kafka.NewProducer(&kafka.ConfigMap{
"bootstrap.servers": "localhost:9097",
"compression.type": "lz4",
"enable.idempotence": true,
"acks": "all",
"linger.ms": 100,
"batch.num.messages": 10000,
"retries": 5,
"retry.backoff.ms": 100,
"go.delivery.reports": false,
})
Consumer Setup
c, err := kafka.NewConsumer(&kafka.ConfigMap{
"bootstrap.servers": "localhost:9097",
"group.id": "bench-go-confluent-[timestamp]",
"auto.offset.reset": "smallest",
"enable.auto.commit": true,
"group.protocol": "consumer",
"fetch.wait.max.ms": 5,
})
3. Franz-Go (Go)
Example Console Startup Output:
🚀 Franz-Go Producer configured and ready (PLAINTEXT, port 9097)
Settings: RequiredAcks=all, linger.ms=100, batch.num.messages=10000 (1MB limit), compression=lz4
🚀 Franz-Go Consumer started (PLAINTEXT, port 9097)...
Settings: group.id=bench-go-franz-178047..., auto.offset.reset=smallest (default), group.protocol=consumer
Producer Setup
opts := []kgo.Opt{
kgo.SeedBrokers("localhost:9097"),
kgo.ProducerLinger(time.Duration(lingerMs) * time.Millisecond),
kgo.ProducerBatchMaxBytes(1000000),
kgo.RequiredAcks(kgo.AllISRAcks()),
kgo.ProducerBatchCompression(kgo.Lz4Compression()),
}
Consumer Setup
opts := []kgo.Opt{
kgo.SeedBrokers("localhost:9097"),
kgo.ConsumerGroup("bench-go-franz-[timestamp]"),
kgo.ConsumeTopics("test-topic"),
kgo.GroupProtocol("consumer"),
}
Repository
kafkaclitest
Owner
Statistic
- 0
- 0
- 0
- 0
- 0
- 8 days ago
- June 3, 2026
License
Links
Synced at
Wed, 03 Jun 2026 08:33:19 GMT
Languages