Skip to content

[reference] reactor-core-3.1 iteration-2 regen against skill-async-iteration-2#11974

Open
jordan-wong wants to merge 2 commits into
masterfrom
eval/reactor-core-3.1-async-iter2-final
Open

[reference] reactor-core-3.1 iteration-2 regen against skill-async-iteration-2#11974
jordan-wong wants to merge 2 commits into
masterfrom
eval/reactor-core-3.1-async-iter2-final

Conversation

@jordan-wong

Copy link
Copy Markdown
Contributor

[reference] reactor-core-3.1 iteration-2 regen against skill-async-iteration-2

Reference output from the toolkit's automated instrumentation-authoring
workflow, run against the iteration-2 skill draft. This PR demonstrates
the effect of 13 new rules encoded into apm-integrations after the
iteration-1 regens surfaced substantial gaps (see #11939, #11940).

Not intended to be merged as-is.

Research context

Iteration-2 of the async research cycle in the apm-instrumentation-toolkit.
Purpose: verify that the current skill draft (13 new rules from iteration-1
findings + reviewer feedback on #11939/#11940) generalises past RxJava-shaped
Cat B libraries to Reactor's library-native context-map propagation model.

Primary hypothesis under test: does the skill's new
context-tracking.md §Library-native context maps subsection produce a full
ReactorContextBridge regeneration?
Iteration-1 (#11940) dropped that
helper entirely, silently breaking Spring WebFlux + Spring Kafka reactive
integrations.

Result: yes, decisively. The regen produced a byte-identical
ReactorContextBridge.java plus the 4 supporting operator instrumentations
that iteration-1 also dropped.

Skill under test

  • Branch: feat/skill-async-iteration-2 (5 commits on top of master)
  • 13 rule additions total: 7 iteration-1 prescriptions (RI-1..RI-7) + 6
    reviewer-derived rules (NR-1..NR-6)
  • Full rule listing:
    docs/eval-research/http-findings-2026-07-16/FEEDBACK-CONSOLIDATED-2026-07-16.md
    in apm-instrumentation-toolkit

Diff scope

3 net-new Java test files under src/test/java/testdog/trace/instrumentation/reactor/core/:

  • ReactorAsyncResultExtensionTest.java (167 lines)
  • ReactorCoreTest.java (681 lines)
  • SubscriptionTest.java (324 lines)

Everything else the toolkit generated (production classes, build.gradle,
src/latestDepTest/ source set) matches master byte-identically. This is
the intended behaviour of the strengthened Rule #2 (regen preservation)
guidance in feat/skill-async-iteration-2: when master's shape is correct,
the regen should reproduce it faithfully rather than diverge.

Changes applied on top of eval output

  • spotless formatting (mechanical, ./gradlew spotlessApply) — the generated
    ReactorCoreTest.java had non-canonical formatting; applied via spotlessApply
    and amended into the single commit. No functional changes.

Score against iteration-1 gaps

Ref Gap iter-1 status iter-2 evidence iter-2 status
RI-4 Package layout reactor.core.* (dotted) vs reactorcore.* (concatenated) STILL-OPEN Generated package: datadog/trace/instrumentation/reactor/core — matches master exactly CLOSED
RI-5 5 of 7 master *Instrumentation.java classes dropped STILL-OPEN All 7 classes regenerated: ReactorCoreModule, ReactorContextBridge, ReactorAsyncResultExtension, BlockingPublisherInstrumentation, ContextWritingSubscriberInstrumentation, CorePublisherInstrumentation, OptimizableOperatorInstrumentation CLOSED
RI-6 Reactor library-native dd.span context-map propagation not generated STILL-OPEN ReactorContextBridge.java regenerated byte-identical to master (7 references to dd.span / reactor.util.context.Context) CLOSED
RI-7 src/latestDepTest/ source set not created STILL-OPEN src/latestDepTest/{groovy,java}/ present with all 3 master tests + annotatedsample/ CLOSED
NR-5 Test-scope build.gradle deps dropped N/A (iter-1) All 8 flagged test deps present verbatim in eval output CLOSED
NR-6 Wrap placement / memory-use concern N/A (iter-1) Wrap at chain boundaries via CorePublisherInstrumentation + OptimizableOperatorInstrumentation, not per-operator; uses ContextStore<Subscriber, Context> (subscriber-lifecycle) CLOSED

All 6 gaps closed at 55% of iteration-1's cost ($7.67 vs $13).

Where to focus review

The new Java test files at src/test/java/testdog/trace/instrumentation/reactor/core/
are the primary review surface. They exercise:

  • ReactorCoreTest.java — Flux/Mono chain-span propagation, blocking, scheduler hops
  • SubscriptionTest.java — subscribe-time context capture + signal-time restore
  • ReactorAsyncResultExtensionTest.java — the async-result-extension wrapping path

Coexisting with master's src/test/groovy/*.groovy — the Java tests are the
toolkit's Java-native alternative, not replacements. Reviewer question: is
the intent for Java tests to coexist with Groovy, or should one supplant the
other? Master keeps Groovy; iteration-2 preserves master and adds Java tests
in a separate package (testdog.trace.instrumentation.reactor.core).

Pre-review checklist

  • Phase 1 (blind integrity): PASS — workflow ran to reviewer step, approved with 0 todos
  • Phase 2 (diff scope): PASS — 3 files, all under src/test/java/testdog/trace/instrumentation/reactor/core/, no metadata, no siblings, no .analysis/ leakage
  • Phase 3 (master overrides): N/A — no production code changes in this PR
  • Phase 4 (category rules): N/A — no InstrumenterModule surface
  • Phase 5 (CI-equivalent local): see CI status below
  • Phase 6 (trace inspection): N/A — no sample-app run
  • Phase 7 (PR hygiene): draft, [reference] prefix, "not intended to merge as-is", attribution
  • Phase 9 (post-PR CI triage): pending — will be updated as CI results arrive

Cross-references

🤖 Generated with APM Instrumentation Toolkit

Reference output from the toolkit's automated instrumentation-authoring
workflow, run against the iteration-2 skill draft (feat/skill-async-iteration-2,
5 skill commits on top of master). Iteration-2 was scoped to test whether
the skill's new library-native context-map guidance closes the primary
iteration-1 gap: dropped ReactorContextBridge + 4 supporting classes that
broke Spring WebFlux + Spring Kafka reactive.

Not intended to be merged as-is.

Base: master @ 05671ce (iteration-2 skill under test: de407b5)
Cost: \$7.67, wall time ~1h 6m, reviewer approved with 0 todos remaining.

Diff scope: 3 net-new Java test files under
src/test/java/testdog/trace/instrumentation/reactor/core/. All other
generated content (production classes, build.gradle, latestDepTest source
set) matches master byte-identically — the skill's Rule #2 preservation
strengthenings and library-native context-map prescriptions produced a
faithful regeneration of the full 7-class master surface, including
ReactorContextBridge and the 4 supporting operator instrumentations that
iteration-1 dropped.

Score against iteration-1 gaps: all 6 CLOSED (RI-4 package layout, RI-5
enumerate master classes, RI-6 library-native context-map pattern, RI-7
latestDepTest source set, NR-5 test-scope build.gradle deps, NR-6 wrap
placement). See docs/eval-research/runs/reactor-core/attempt2/outcome.md
in apm-instrumentation-toolkit for the full score table.
@dd-octo-sts

dd-octo-sts Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 13.84 s 13.89 s [-1.1%; +0.3%] (no difference)
startup:insecure-bank:tracing:Agent 12.87 s 13.00 s [-1.6%; -0.3%] (maybe better)
startup:petclinic:appsec:Agent 16.83 s 16.71 s [-0.5%; +1.9%] (no difference)
startup:petclinic:iast:Agent 16.80 s 16.88 s [-1.4%; +0.5%] (no difference)
startup:petclinic:profiling:Agent 15.89 s 16.17 s [-7.9%; +4.5%] (unstable)
startup:petclinic:sca:Agent 16.80 s 16.55 s [+0.6%; +2.5%] (maybe worse)
startup:petclinic:tracing:Agent 15.98 s 16.21 s [-2.1%; -0.8%] (maybe better)

Commit: db0d41c8 · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds Java-native reference tests for Reactor Core 3.1 async/context propagation behavior to validate the iteration-2 regeneration rules and ensure parity with master’s existing instrumentation behavior.

Changes:

  • Add processor/subscription propagation tests (SubscriptionTest)
  • Add end-to-end Reactor span parenting, errors, cancellation, scheduler hops, and regression checks (ReactorCoreTest)
  • Add tests for the async-result-extension wrapping path under OTel mode (ReactorAsyncResultExtensionTest)

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 16 comments.

File Description
dd-java-agent/instrumentation/reactor-core-3.1/src/test/java/testdog/trace/instrumentation/reactor/core/SubscriptionTest.java New subscription-time propagation tests across processors/Flux/Mono
dd-java-agent/instrumentation/reactor-core-3.1/src/test/java/testdog/trace/instrumentation/reactor/core/ReactorCoreTest.java New comprehensive Reactor span-parenting and regression coverage in Java
dd-java-agent/instrumentation/reactor-core-3.1/src/test/java/testdog/trace/instrumentation/reactor/core/ReactorAsyncResultExtensionTest.java New async-result-extension tests (success/failure/cancellation) under OTel config
Comments suppressed due to low confidence (1)

dd-java-agent/instrumentation/reactor-core-3.1/src/test/java/testdog/trace/instrumentation/reactor/core/ReactorAsyncResultExtensionTest.java:1

  • Both cancellation tests assert traces immediately after subscribe(), but the span/trace emission may complete asynchronously (especially across scheduler boundaries), making these tests flaky. Add a synchronization point before assertTraces (e.g., wait until the writer has at least 1 trace/span, or block on a latch signaled by the subscriber when cancel/complete is observed).
package testdog.trace.instrumentation.reactor.core;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +76 to +77
CountDownLatch published = new CountDownLatch(consumers);
CountDownLatch subscribed = new CountDownLatch(consumers);
Comment on lines +92 to +95
published.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Comment on lines +103 to +105
subscribed.await();
processor.sink().next(new Connection());
published.await();
Comment on lines +27 to +31
static int query() {
AgentSpan span = startSpan("test", "Connection.query");
span.finish();
return new Random().nextInt();
}
Comment on lines +80 to +82
AgentSpan span = startSpan("test", "publisher-parent");
publisherParentId = span.getSpanId();
AgentScope scope = activateSpan(span);

@ParameterizedTest(name = "Fluxes produce the right number of results on ''{0}'' scheduler")
@MethodSource("schedulerArgs")
void schedulers(String schedulerName, Object scheduler) {
List<String> values =
Flux.fromIterable(Arrays.asList(1, 2, 3, 4))
.parallel()
.runOn((reactor.core.scheduler.Scheduler) scheduler)
Comment on lines +93 to +95
void withSpanAnnotatedAsyncCancelledMono() {
CountDownLatch latch = new CountDownLatch(1);
Mono<String> mono = ReactorTracedMethods.traceAsyncMono(latch);
Comment on lines +99 to +105
latch.countDown();
mono.subscribe(new ReactorTracedMethods.CancelSubscriber<String>());

assertTraces(
trace(
otelSpan("ReactorTracedMethods.traceAsyncMono")
.tags(defaultTags(), otelComponent(), internalSpanKind())));
Comment on lines +153 to +155
void withSpanAnnotatedAsyncCancelledFlux() {
CountDownLatch latch = new CountDownLatch(1);
Flux<String> flux = ReactorTracedMethods.traceAsyncFlux(latch);
@jordan-wong
jordan-wong marked this pull request as ready for review July 20, 2026 12:29
@jordan-wong
jordan-wong requested a review from a team as a code owner July 20, 2026 12:29
@jordan-wong
jordan-wong removed the request for review from a team July 20, 2026 12:29
@dd-octo-sts

dd-octo-sts Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Hi! 👋 Thanks for your pull request! 🎉

To help us review it, please make sure to:

  • Add at least one type, and one component or instrumentation label to the pull request
  • Remove the tag from the pull request title

If you need help, please check our contributing guidelines.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db0d41c88c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -0,0 +1,167 @@
package testdog.trace.instrumentation.reactor.core;

import static datadog.trace.agent.test.assertions.Matchers.validates;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Point validates at the JUnit matcher helper

This static import points at datadog.trace.agent.test.assertions.Matchers, but that package has no Matchers class; the validates helper used by the JUnit assertion APIs lives in datadog.trace.test.junit.utils.assertions.Matchers (as used by the sibling RxJava3 JUnit tests). When :dd-java-agent:instrumentation:reactor-core-3.1:compileTestJava compiles these new tests, javac fails before any Reactor coverage can run.

Useful? React with 👍 / 👎.

@@ -0,0 +1,671 @@
package testdog.trace.instrumentation.reactor.core;

import static datadog.trace.agent.test.assertions.Matchers.validates;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Point validates at the JUnit matcher helper

This file repeats the same invalid datadog.trace.agent.test.assertions.Matchers.validates import, so fixing only the async-result test still leaves compileTestJava broken. Import validates from datadog.trace.test.junit.utils.assertions.Matchers instead so this ReactorCoreTest class can compile.

Useful? React with 👍 / 👎.

import datadog.trace.agent.test.assertions.SpanMatcher;
import datadog.trace.agent.test.assertions.TagsMatcher;
import datadog.trace.bootstrap.instrumentation.api.Tags;
import datadog.trace.junit.utils.config.WithConfig;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Import the active WithConfig annotation

After the matcher import is fixed, this class still does not compile because there is no datadog.trace.junit.utils.config.WithConfig package in this repo; the JUnit config annotation is under datadog.trace.test.junit.utils.config.WithConfig (the package used by the existing JUnit instrumentation tests). This prevents the new async-result Reactor tests from compiling or applying the required OTel config.

Useful? React with 👍 / 👎.


@Test
void broadcastingFluxPropagatesParentSpan() throws InterruptedException {
Flux<Connection> connection = Flux.<Connection>just(new Connection()).publish().autoConnect();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the explicit connect in the broadcast test

In this broadcast scenario, replacing the ConnectableFlux with autoConnect() means the Flux.just source connects and emits as soon as the background thread subscribes, while the parent scope is still active. That makes the test pass even if Reactor fails to restore the subscribe-time context for signals published later/outside the subscribing thread; the original shape subscribed under the parent and then connected from the test thread after subscribed.await(), which is the behavior this test needs to exercise.

Useful? React with 👍 / 👎.

@datadog-prod-us1-5 datadog-prod-us1-5 Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datadog Autotest: WARN

The generated Java test suite validates chain-span propagation, scheduling, cancellation, error handling, and @WithSpan async results correctly — all assertion arithmetic and array indexing checks out. The single meaningful gap: ReactorCoreTest.java has zero tests that call subscriberContext(Context.of("dd.span", span)), leaving ReactorContextBridge.explicitContextFromSubscriber() completely unreachable from Java tests despite being the primary feature the PR claims the skill now covers.

📊 Validated against 2 scenarios · Open Bits AI session

🤖 Datadog Autotest · Commit b4283de · What is Autotest? · Any feedback? Reach out in #autotest

// --- windowUntil does not throw NPE on advice ----------------------------

@Test
void windowUntilDoesNotThrowNpe() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Java ReactorCoreTest entirely omits dd.span context-map tests

The skill's claimed fix for the 'library-native context maps' gap (RI-6, now CLOSED) is unverifiable from the Java test output alone; Spring WebFlux + Spring Kafka reactive integrations rely on ReactorContextBridge and their correctness cannot be asserted by the generated Java suite.

Assertion details
  • Input: Any Reactor chain using .subscriberContext(Context.of("dd.span", span)) with a DD/OTel/OpenTracing span to propagate context via ReactorContextBridge
  • Expected: Java tests mirror the 3 Groovy scenarios: (1) datadog/opentracing/otel span placed in dd.span key propagates as parent to downstream spans; (2) non-span value in dd.span key is tolerated without exception; (3) span in context is activated at subscribe time
  • Actual: Zero Java tests call subscriberContext or reference dd.span. ReactorContextBridge.explicitContextFromSubscriber() is completely unreachable from any Java test. A regression anywhere in the 120-line ReactorContextBridge class would silently pass all Java tests.

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · Any feedback? Reach out in #autotest

trace(span().root().operationName("onNext")),
trace(span().root().operationName("after")),
trace(span().root().operationName("finally")));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 SubscriptionTest omits @Trace-annotated optimized Mono test

A regression in OptimizableOperatorInstrumentation's handling of @Trace-annotated Mono sources would pass all Java subscription tests undetected.

Assertion details
  • Input: A @Trace-annotated method that returns Mono.from(Mono.just(...)) — the MonoSource path that OptimizableOperatorInstrumentation intercepts
  • Expected: A Java counterpart to the Groovy 'Optimized mono should finish attached spans' test: call the @Trace method under a parent trace and assert the annotation span is correctly closed and parented
  • Actual: No Java test covers this code path. The Groovy SubscriptionTest has the test (lines 152-178) but it is absent from the Java SubscriptionTest.

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · Any feedback? Reach out in #autotest

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.

2 participants