Skip to content

Fix --otlp-exporter-compression none being ignored; store compression as a plain CompressionEncoding - #375

Merged
mheffner merged 2 commits into
rotel-dev:mainfrom
mheffner:perf-otlp-compression-none
Oct 30, 2026
Merged

mheffner merged 2 commits into
rotel-dev:mainfrom
mheffner:perf-otlp-compression-none

Conversation

@mheffner

Copy link
Copy Markdown
Member

Problem

--otlp-exporter-compression none had no effect. The OTLP exporter always gzipped request bodies and sent grpc-encoding: gzip or Content-Encoding: gzip.

The exporter config stored compression as Option<CompressionEncoding>, but the CLI value is the enum CompressionEncoding { Gzip, None }. .into() converted CompressionEncoding::None into Some(CompressionEncoding::None), and the request path only checked compression.is_some(), so "none" still enabled gzip.

Fix

  • The OTLP exporter config and request code now hold a plain CompressionEncoding (default Gzip) instead of an Option.
  • The is_some() checks for the gRPC header, the HTTP header and body encoding are now explicit checks for CompressionEncoding::Gzip.
  • The CLI value is passed straight through to with_compression_encoding.
  • The per-signal overrides (--otlp-exporter-{traces,metrics,logs}-compression) stay Option, where unset means "inherit the base setting."
  • CompressionEncoding now derives PartialEq.

Kafka, ClickHouse and Parquet already use plain enums, so they don't have this bug.

Breaking change

OTLPExporterConfig::with_compression_encoding now takes CompressionEncoding instead of Option<CompressionEncoding>. External callers passing Some(x) should pass x, and callers passing None should pass CompressionEncoding::None.

Testing

  • New unit tests check that none produces no compression and no gzip headers, and gzip produces both, over gRPC and HTTP.
  • A new test covers a per-signal override against inheriting the base setting.
  • cargo fmt is clean, and cargo test passes.
  • cargo clippy --all-targets adds no new warnings. It already fails on main because of an existing bench config_builder arity error.
  • In benchmarks, compression none now uses about 51% less CPU per span than the old gzip behavior.

mheffner and others added 2 commits October 26, 2026 18:57
OTLPExporterBaseArgs::try_into_exporter_config passed
`self.compression.into()` to with_compression_encoding(), which goes
through the blanket `From<T> for Option<T>` and turns
CompressionEncoding::None into Some(CompressionEncoding::None). The
request builder only checks `compression.is_some()`, so "none" (and the
per-signal --otlp-exporter-{traces,metrics,logs}-compression none)
still gzip-compressed every request and sent grpc-encoding/
Content-Encoding: gzip. Verified at a mock sink: 62/62 requests arrived
gzip-encoded with --otlp-exporter-compression none.

Map CompressionEncoding::None to no compression and add a test.

Benchmark (rotel-bench, Intel N95, 5 runs per variant, ABBA, mean ±
stddev), --otlp-exporter-compression none (what the rotel-otel-wrapper
load-test setup passes):

  traces 50k spans/s, 2 cores    CPU/span 7.33±0.04 -> 5.60±0.02 %/10k (-23.6%)
                                 RSS 35.2 -> 33.8 MiB (-3.8%)
  traces max, 1 core, 1000/req   277.2k±0.1k -> 434.6k±3.7k spans/s (+56.8%)
                                 CPU/span -50.9%, p50 -26%, p99 -31%, RSS -27%
  Wire bytes per span 19.7 -> 148.6 (uncompressed, as requested).
  Default (gzip) configuration unchanged (traces-otlp/traces-max within
  ±0.4%, same wire bytes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the previous workaround (mapping CompressionEncoding::None to
Option::None in OTLPExporterBaseArgs) with a simpler model: the OTLP
exporter config and RequestBuilderConfig now hold a plain
CompressionEncoding (default Gzip) instead of Option<CompressionEncoding>.

The original bug came from the Option: `self.compression.into()` turned
CompressionEncoding::None into Some(CompressionEncoding::None), and the
request builder only checked `compression.is_some()`, so "none" still
sent gzip bodies and grpc-encoding/Content-Encoding: gzip headers. The
gRPC/HTTP header and body-encoding checks now compare against
CompressionEncoding::Gzip explicitly, and the CLI value is passed
straight through to with_compression_encoding().

Per-signal --otlp-exporter-{traces,metrics,logs}-compression flags stay
Option (unset = inherit the base setting) and resolve to a plain
CompressionEncoding in build_*_config.

Tests: CLI none/gzip pass-through, per-signal override/inherit, and
gRPC + HTTP request headers and bodies (uncompressed frame/body for
None, gzip flag/magic for Gzip).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mheffner
mheffner merged commit 0858a33 into rotel-dev:main Oct 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant