Skip to content

fix(sinks): use real wire bytes for OTLP batch sizing in the http sink#25882

Draft
vladimir-dd wants to merge 1 commit into
vectordotdev:masterfrom
vladimir-dd:otlp-http-batch-sizer-wire-bytes
Draft

fix(sinks): use real wire bytes for OTLP batch sizing in the http sink#25882
vladimir-dd wants to merge 1 commit into
vectordotdev:masterfrom
vladimir-dd:otlp-http-batch-sizer-wire-bytes

Conversation

@vladimir-dd

Copy link
Copy Markdown
Contributor

Summary

HttpBatchSizer::size() (used by the generic http sink, and by extension the opentelemetry sink which wraps it) falls back to Event::size_of() for any serializer it doesn't special-case. For the OTLP protobuf serializer, that in-memory size_of() estimate overstates the real wire size by roughly an order of magnitude, because it's sizing the much larger JSON-shaped Event::Log representation rather than the compact protobuf payload actually sent.

Since max_bytes batch accounting is driven by this estimate, batches close far earlier than the configured byte budget intends — in a production deployment we measured this causing ~6.6x more HTTP requests per second than the byte cap should have allowed for a given event stream, because each batch was hitting the (over-estimated) size ceiling long before it reached the real 380KB configured max.

This PR adds an OTLP-specific branch to HttpBatchSizer::size() that clones the configured serializer and actually encodes the event to get its true protobuf-encoded length, falling back to size_of() only if encoding fails. This mirrors exactly what the real per-request encoder does, so batch-close decisions now reflect the real wire size.

Why not just special-case a cheaper estimate?

OTLP protobuf encoding doesn't have a cheap structural shortcut analogous to JSON's estimated-size helpers — arbitrary numbers of repeated fields, varint-encoded lengths, and attribute maps mean an accurate estimate essentially requires doing the encode. Encoding here is cheap in practice (Arc-backed clones of the serializer/item, no additional allocation churn beyond the actual encode buffer), and it's the same work the request path does moments later anyway.

Test plan

  • Added a unit test (otlp_batch_size_matches_real_wire_bytes_and_beats_size_of) asserting the sizer's reported size exactly matches a real OTLP-protobuf encode of the same event, and is meaningfully smaller than size_of().
  • Built a custom Vector/OPW image with this fix and canary-tested it against a live production-like OTLP metrics pipeline; confirmed request rate dropped to the expected ~1 req/s (previously ~6.6 req/s) with zero errors, then rolled out to a full 64-pod deployment and validated for an extended period before reverting the test deployment.

@github-actions github-actions Bot added the domain: sinks Anything related to the Vector's sinks label Jul 17, 2026
@datadog-vectordotdev

Copy link
Copy Markdown

Pipelines

⚠️ Warnings

🚦 1 Pipeline job failed

Changelog | validate-changelog   View in Datadog   GitHub Actions

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 55a9ac9 | Docs | Give us feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain: sinks Anything related to the Vector's sinks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant