Skip to content

Performance

Fanout has a publication candidate, not an official throughput claim yet. The distinction is visible beside the number because provenance is part of a benchmark result, not a footnote added after it.

On a machine with eight logical CPUs and 15.6 GiB of memory, Fanout v2026.8.11 accepted 296,196 OpenTelemetry items per second for five minutes. The load contained traces, logs and metrics at the same time, and Fanout and the load generators shared the host.

WorkloadFanout accepted/sOffered/sAverage Fanout CPUPeak RSS
Traces, spans220,324320,0002.45 cores1.2 GiB
Logs, records187,303320,0003.04 cores1.3 GiB
Metrics, data points209,638320,0001.69 cores1.1 GiB
Three-signal short peak355,938960,0004.57 cores3.0 GiB
Three-signal sustained296,196960,0004.90 cores9.4 GiB

Accepted throughput is the change in Fanout’s own fanout_ingest_rows_total counter over the measured phase. It is not the rate requested or reported by the generator. Across the complete saturation suite, Fanout accepted 153.8 million items.

The run passed its functional quality gates: every generator exited successfully, Fanout reported zero dropped rows and no restart, and fanout repair verify validated the authoritative Parquet store.

Generator load, not a Fanout scaling curve

Section titled “Generator load, not a Fanout scaling curve”

The chart on the landing page plots the same run’s generator sweep: one, two, four, eight and sixteen telemetrygen processes per signal, doubling at every step. Accepted throughput rose from 38,853 items per second at one process per signal to the 355,938 short peak at sixteen. That is a 9.2x gain for a 16x increase in generators, and the shape is the point—below sixteen processes the load generators, not Fanout, set the rate. Read it as evidence that the saturating configuration was reached, not as a scaling law for Fanout itself. The intermediate process counts are plotted from the same run and their exact values ship with the raw artifacts.

What this number does—and does not—measure

Section titled “What this number does—and does not—measure”

This is a three-signal ingest result: spans, log records and metric data points were written concurrently. It is not a read/write result. Calling it a mixed workload would imply that dashboard queries ran beside ingest, which they did not.

The official read-under-write result will separately report requested, completed, failed and shed query rates plus p50, p95 and p99 latency. Until that result passes, Fanout makes no claim about interactive query capacity at the headline write rate.

The generators shared the Fanout host, so generator CPU and memory contention can make the observed result lower than an isolated-server ceiling. Synthetic telemetrygen payloads are also highly compressible, so this run is not a storage-sizing formula for production data.

Before the candidate becomes Fanout’s official headline, it has to ship with:

  • the Fanout release artifact and checksum plus the benchmark-driver commit;
  • complete machine, network, container and Fanout configuration;
  • the raw report.json, report.md and samples.jsonl from that same run;
  • an authenticated OTLP path matching the current release;
  • at least three publish-preset repetitions and their variability;
  • separate ingest-only and read-under-write results;
  • the query completion rate and latency distribution beside any read claim;
  • limitations stated next to the result rather than hidden at the bottom.

The independent Fanout Bench project drives the official OpenTelemetry telemetrygen client, counts rows accepted by Fanout, and emits raw JSON plus a publication-ready report. It can add authenticated dashboard reads on top, which is the part that matters. Parquet ingest, indexed trace reads, and DuckDB analytics have separate coordination paths, but they still compete for the same CPU, memory bandwidth, filesystem cache, and disk. Maintenance adds native Parquet merge work and short reader-exclusive namespace changes. An ingest-only number therefore still overstates what an instance does while anyone is looking at it.

Create a short-lived ingest token through first-admin setup, configure Fanout with a dedicated FANOUT_METRICS_TOKEN, then expose that same metrics token to the harness as FANOUT_BENCH_METRICS_TOKEN. Metrics are private by default. A local run starts with:

Terminal window
export FANOUT_BENCH_INGEST_TOKEN=''
export FANOUT_BENCH_METRICS_TOKEN=''
fanout-bench run \
--endpoint localhost:4317 \
--metrics-url http://localhost:7520/-/metrics \
--insecure \
--same-host

The harness repository documents signal-specific saturation, sustained three-signal load, read-under-write latency, machine identity, and publication rules. For a publication-quality run, use the publish preset and retain all three repetitions:

Terminal window
fanout-bench run \
--host your-benchmark-host \
--fanout-version v2026.8.11 \
--preset publish \
--repeat 3 \
--output results/fanout-v2026.8.11

For capacity, the useful signals are the ones an instance emits about itself: telemetry drops, sustained query latency, readiness failures, and free disk. Troubleshooting lists where they are.