Repository navigation
HDDS-17350. Build the OpenTelemetry SDK lazily in application-aware tracing mode - #11723
yandrey321 wants to merge 3 commits into
Conversation
…racing mode Generated-by: Claude Code (Claude Opus 5.5)
jojochuang
left a comment
There was a problem hiding this comment.
One compatibility finding in deferred tracing initialization, verified against the PR source and OpenTelemetry 1.67.0 implementation. Tests were not run.
Generated-by: Codex (GPT-6)
| } | ||
| try { | ||
| if (GlobalOpenTelemetry.isSet() && isRealGlobal(GlobalOpenTelemetry.get())) { | ||
| tracer = GlobalOpenTelemetry.get().getTracer(GLOBAL_TRACER_NAME); |
There was a problem hiding this comment.
[P2] Handle the default global installed during deferred initialization
After deferred initialization, another library can call GlobalOpenTelemetry.get() before Ozone's first traced operation. OpenTelemetry 1.67.0 installs a wrapper around its no-op implementation, which passes isRealGlobal() because the wrapper differs from OpenTelemetry.noop(). This branch adopts the no-op tracer and permanently clears pending initialization, silently losing subsequent Ozone child spans even with valid parents. Previously, Ozone had already installed its functioning SDK before that read. Handle this wrapped default global and add coverage for initTracing(), GlobalOpenTelemetry.get(), then a valid parent span.
Reference: OpenTelemetry 1.67.0 GlobalOpenTelemetry.
There was a problem hiding this comment.
fixed. In 1.67.0, GlobalOpenTelemetry.get() with nothing registered installs OpenTelemetry.noop() wrapped in ObfuscatedOpenTelemetry, which passed the identity check. isRealGlobal() now also rejects a global whose tracer provider is the no-op one. Since set() can't replace an existing global, initOzoneSdk() now registers Ozone's SDK only when none is set, and otherwise uses it unregistered. The same misdetection affected the eager path (tracing enabled after an early GlobalOpenTelemetry.get()), which this fixes too. New tests cover initTracing() → GlobalOpenTelemetry.get() → valid parent, asserting that Ozone creates its own child span (a no-op tracer would return the parent), and the enabled case.
|
Thanks @yandrey321 for the patch. Created HDDS-17426 to mitigate in acceptance tests until this improvement lands. |
Co-authored-by: Codex (GPT-6) Change-Id: I54bf5164fa3378f56160a48f3f66ddf3ee41ba73
What changes were proposed in this pull request?
Problem. Every Ozone client calls
TracingUtil.initTracing(). With tracing disabled butozone.tracing.client.application-aware=true(both defaults, HDDS-13803), and noGlobalOpenTelemetryregistered bythe application,
initialize()builds Ozone's OpenTelemetry SDK right away:BatchSpanProcessorwith its export thread;SdkTracerProvider, registered asGlobalOpenTelemetry.In this mode Ozone only creates a span under a valid parent span, which most client processes never have, so all
of this usually goes unused. It costs about 80 ms and several hundred classes in every cold client: YARN containers,
CLI commands, MapReduce tasks.
Implementation idea. In application-aware mode without an application
GlobalOpenTelemetry,initialize()nowonly records that an SDK may be needed. The first span created under a valid parent completes the initialization,
once, under the class lock:
GlobalOpenTelemetryregistered since then, if there is one;Spans are created under a parent in three places:
buildSpan(a valid current span),importAndCreateSpan(apropagated parent) and
createActivatedSpanFromW3cHttpHeaders(a valid remote parent). Each callsinitPendingSdk()before using the tracer.
hasUsableTracer()counts a pending SDK as usable, soisTracingActive()andcreateProxy()behave as before.Key decisions.
ozone.tracing.enabled=truethe SDK is still built at init, becauseOzone can start root spans at once.
GlobalOpenTelemetryaftercreating the Ozone client now gets its spans into its own SDK. Before, Ozone had already installed its own SDK as
the global, so the application's
GlobalOpenTelemetry.set()failed.Side effects.
GlobalOpenTelemetryat the first traced call instead of atclient creation. A co-resident library that reads
GlobalOpenTelemetrybefore then sees the default no-op instance,as in a process with tracing turned off.
Generated-by: Claude Code (Claude Opus 5.5)
What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/HDDS-17350
How was this patch tested?
Unit + integration tests: https://github.com/yandrey321/ozone/actions/runs/40352078370
New tests in
TestTracingInitModes, all with no application global unless stated:counts as active and no root span is created;
GlobalOpenTelemetrythat the application registers after init is adopted; a recording exporter shows thatthe child span went to the application's SDK;
A mutation check (always building the SDK at init) fails three of the four tests.
Existing tests: all
TestTracing*andTestSpanSamplingtests in hdds-common (43), andTestFailoverWithSCMHA, which wraps SCM clients withTracingUtil.createProxy.Measurement, on JDK 17 (17.0.20.1), same build. The change has no switch back to the eager behaviour, so the
eager arm enables tracing, which builds the same SDK at init:
TracingUtil.initTracing()in a fresh JVM, 9 runs: 98 ms and 772 extra classes when the SDK is built at init →16 ms and 149 classes in application-aware mode now (−82 ms). The rest is the tracing configuration and the
OpenTelemetry API.
Cold
ofs://client on the secureozonesecurecompose cluster (create the client, getFileStatus, read 64 KB,close), 10 interleaved rounds: the default application-aware mode takes 1,244 ms, against 1,239 ms with
ozone.tracing.client.application-aware=false(tracing off). The 5 ms difference is within noise, so the defaultno longer pays for tracing.