Repository navigation
Conversation
CarrierSetter gains setAscii, which by default sets the value's String form, so a setter for a carrier that stores bytes can take them as built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was formatted through hex strings and a StringBuilder into a String, then encoded again by byte carriers such as Kafka headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A per-item conversion to fit an API, or a convenience call that allocates or scans, sits on the line reviewers treat as outside their control. Add a universal check to read the callee, and a Java entry for String/byte[] conversions at carrier boundaries such as Kafka headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
🟢 Java Benchmark SLOs — All performance SLOs passed
PR vs. master results
Commit: Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion. |
This comment has been minimized.
This comment has been minimized.
Two digits per division, as Long.toString does, so a 19-digit id costs no more than the JDK's formatting, and AsciiDecimal returns arrays sized to the digits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single-number value needs no builder: the setter takes its bytes, which Kafka headers keep as they are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e reused span A reused span lets values cached on its ids be built once, which flatters caching. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What Does This Do
Injected propagation header values can now be built directly as ASCII bytes, so carriers that store bytes (Kafka headers) take them without a
Stringin between. The W3Ctraceparentand the Datadog trace id, parent id, sampling priority and end-to-end start time are now built this way.AsciiValueBuilder(components/context) appends chars, ASCII text, fixed-width hex and decimal straight into abyte[]. Sized to the value's length,toBytes()returns its own array without copying.toString()gives theStringform.AsciiDecimalwrites alongas decimal digits into an array sized exactly, two digits per division asLong.toStringdoes. A single-number header uses it directly, with no builder.CarrierSetter.setAsciihas two new default methods, one taking a builder and one takingbyte[]. Both set the value'sStringform, so every existing setter keeps working unchanged. The Kafka setters override both to add the bytes to the header as they are.Before, each value went through a
String, often built from hex or decimalStrings and aStringBuilder, and the Kafka setter then encoded it again withgetBytes. Now the bytes Kafka keeps are the only array.Motivation
In a profile of
kafka-producer-perf-testunder the agent, writing header values was about 15% of the sending thread's CPU, and byte-array copies were about 22% of allocation. Most of that came from building header values asStrings and then encoding them to bytes.KafkaInjectBenchmarkinjects into newRecordHeaders(JDK 17, 3 forks, per inject). Itsshapeparameter sets which span each inject is for:reusedis one span every time (a best case for values cached on its ids),newTraceis a new trace per inject (a producer outside any request), andfanOutis a new child span of one trace per inject (a request sending many messages).The
traceparentstep, measured on a reused span without span baggage:And the Datadog headers on top of it:
Together that's about 360 B less per send, with times unchanged within noise.
Additional Notes
Stringpath caches the trace id's decimal form for the trace; the byte path formats it on each inject. A per-trace byte cache wouldn't help: Kafka keeps and exposes each header's array, so every header needs its own copy anyway, and copying 19 cached bytes costs about as much as writing 19 digits (comparefanOutwithnewTrace). String carriers keep the existing cache.toBytes(), and an array passed tosetAsciimust not be reused.originstays aString, since it can be user-supplied.Strings get the same values through the default methods, as oneStringbuilt from the bytes.tracestateandx-datadog-tags, which go through the propagation tags codecs.String↔byte[]at carrier boundaries, with the Kafka patterns measured here and in Skip context extraction for Kafka records without headers #13177 and Only remove a Kafka header before injecting it if it's already there #13181. The rubric listsAsciiValueBuilderandAsciiDecimalas the fix, so they land together.Stacked on #13181.
🤖 Generated with Claude Code