[reference] R2DBC net-new instrumentation (io.r2dbc:r2dbc-spi 1.0.0, toolkit output) - #12032
[reference] R2DBC net-new instrumentation (io.r2dbc:r2dbc-spi 1.0.0, toolkit output)#12032jordan-wong wants to merge 4 commits into
Conversation
Reference PR from toolkit net-new generation (no prior dd-trace-java module exists for R2DBC, so this is not a blind regen). Generated against io.r2dbc:r2dbc-spi:1.0.0.RELEASE. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The advice hooks io.r2dbc.spi.Statement/Connection/Batch interfaces,
which are structurally unchanged across the 0.8.x/0.9.x/1.0.x
releases -- assertInverse was asserting that all versions outside
[1.0.0.RELEASE,) must fail muzzle, which is false for the older-but-
compatible SPI releases and caused
muzzle-AssertFail-io.r2dbc-r2dbc-spi-{0.8.6,0.9.1}.RELEASE to fail
CI ("unexpectedly passed Muzzle validation").
This does not touch the hook point -- R2DBC's core finding (missing
db.name/peer.hostname/db.user/network.destination.port from hooking
ConnectionMetadata instead of ConnectionFactoryOptions, see
docs/eval-research/hypotheses/r2dbc.md on the toolkit repo) is
unrelated and unaddressed here on purpose.
Verified locally: `./gradlew :dd-java-agent:instrumentation:r2dbc:r2dbc-1.0:muzzle`
BUILD SUCCESSFUL.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Integration list differs from metadata/agent-jar-checks.properties --
super("r2dbc") registered in R2dbcInstrumenterModule.java but the
golden file was never updated. Inserted alphabetically between
quartz and ratpack.
Hand-edited rather than running the Gradle golden-file-update task
directly, but verified via `./gradlew :dd-java-agent:verifyAgentJarIntegrations`
BUILD SUCCESSFUL.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎯 Code Coverage (details) 🔗 Commit SHA: ada5972 | Docs | Datadog PR Page | Give us feedback! |
🟢 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. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 368d2094ce
ℹ️ 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".
| // Connection may be closed or metadata unavailable | ||
| } | ||
| if (INJECT_COMMENT) { | ||
| sql = R2dbcSQLCommenter.inject(sql, null, dbType, null, null, null); |
There was a problem hiding this comment.
Pass an actual traceparent for DBM full mode
When DD_DBM_PROPAGATION_MODE=full, this is the only point where the SQL text is modified, but the traceParent argument is always null because the R2DBC span is not created until execute(); the later execute advice only sets _dd.dbm_trace_injected and cannot update the SQL. The resulting DBM comment has no traceparent, so full-mode DBM cannot correlate the database query with the APM span despite the span being tagged as injected; delay comment injection until a span exists or avoid setting the injected tag unless a traceparent was included.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
No code change — recorded as a documented finding, not fixed on this branch. This is a [reference] not-for-merge research PR, and this is its central instrumentation-logic finding (the missing traceparent + broader connection-metadata gap): it can't be fixed without moving comment injection out of createStatement() to after the span exists, which is the same span-lifecycle restructure as the defer-until-subscribe finding. OpenTelemetry solves it by hooking ConnectionFactories.find() and injecting via a propagator-driven commenter — side-by-side comparison with line refs: https://github.com/DataDog/apm-instrumentation-toolkit/blob/eval/java/docs/eval-research/r2dbc-otel-comparison.md . Captured in the PR body and docs/eval-research/hypotheses/r2dbc.md.
| public void onComplete() { | ||
| try { | ||
| DECORATE.beforeFinish(span); | ||
| span.finish(); |
There was a problem hiding this comment.
Keep spans open through R2DBC result consumption
For R2DBC callers that consume the returned Result later (for example Mono.from(statement.execute()).flatMap(result -> result.map(...)) or getRowsUpdated()), the outer Publisher<Result> can complete before the row/update-count Publisher is consumed, but this line finishes the database span at that outer completion. Errors and latency that occur while consuming rows or update counts are then missed; wrap the emitted Result consumption Publishers or otherwise defer finishing until the result stream terminates.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
No code change — documented finding, not fixed. Correct: the span finishes when the outer Publisher<Result> completes, so errors/latency during row/update-count consumption (e.g. flatMap(result -> result.map(...)), getRowsUpdated()) are missed. Fixing means wrapping the emitted Result's downstream consumption publishers — coupled to the same span-lifecycle restructure as the defer-until-subscribe finding above. Recorded as an instrumentation-logic finding in the PR body + hypotheses/r2dbc.md; left as-generated on this reference PR.
|
|
||
| String sql = info != null ? info.getSql() : null; | ||
|
|
||
| AgentSpan span = startSpan("r2dbc", R2DBC_QUERY); |
There was a problem hiding this comment.
Defer R2DBC spans until subscription
When clients assemble a reactive pipeline without immediately subscribing (common with Spring/Reactor, or if a branch never subscribes), this starts a span during Statement.execute() even though no database work has begun. That span is attached to the assembly-time context and can stay open forever if the publisher is never subscribed, or record inflated latency if subscribed later; create/decorate the span in the returned publisher's subscribe path instead.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
No code change — documented finding, not fixed. Correct diagnosis: the span is started in execute()'s OnMethodEnter rather than in the returned publisher's subscribe(), so an assembled-but-never-subscribed pipeline leaks the span or records inflated latency. Fixing this = moving span creation/decoration into TracingPublisher.subscribe(), a fundamental restructure of the module's approach; on a reference PR that would destroy the artifact this experiment exists to show. Recorded as an instrumentation-logic finding in the PR body + docs/eval-research/hypotheses/r2dbc.md rather than patched.
Codex review (PR #12032): when a returned Publisher is cancelled before onComplete/onError (take(1), timeout, disconnected request), TracingSubscriber passed the driver's Subscription straight through, so cancellation was never observed and the span created in execute() could stay open indefinitely. Wrap the Subscription (named TracingSubscription helper) so cancel() finishes the span, guarded by an AtomicBoolean shared with onComplete/onError so a cancel racing a terminal signal can't double-finish. Registered the new helper class in R2dbcInstrumenterModule.helperClassNames() (muzzle requires it). Verified: compileJava + muzzle + module test suite all green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
| final String originalSql = sql; | ||
| String dbType = null; | ||
| try { | ||
| ConnectionMetadata metadata = connection.getMetadata(); |
There was a problem hiding this comment.
Hook-point difference vs OpenTelemetry (primary). This reads connection identity from Connection.getMetadata() (io.r2dbc.spi.ConnectionMetadata), which by SPI contract exposes only getDatabaseProductName()/getDatabaseVersion() — no host/port/database/user. OTel Java instead hooks io.r2dbc.spi.ConnectionFactories.find(ConnectionFactoryOptions) (R2dbcInstrumentation.java#L25-L34) and captures ConnectionFactoryOptions at factory-creation, then threads it forward per query in DbExecution (DbExecution.java#L84-L97) — which is why OTel can populate db.namespace/server.address/server.port/db.user and this cannot. For a shippable integration, the hook point likely needs to move to the connection factory. Full side-by-side: docs/eval-research/r2dbc-otel-comparison.md (toolkit repo).
| // Connection may be closed or metadata unavailable | ||
| } | ||
| if (INJECT_COMMENT) { | ||
| sql = R2dbcSQLCommenter.inject(sql, null, dbType, null, null, null); |
There was a problem hiding this comment.
DBM injection difference vs OpenTelemetry. Four of six args to R2dbcSQLCommenter.inject(...) are null here — dbService, hostname, dbName, and crucially traceParent (last arg). So even in full DBM mode the SQL comment carries no traceparent, and DB-side query samples cannot be correlated to the APM trace. Root cause is the same as the hook-point comment above: this runs at createStatement() time, before the span exists, so there is no traceparent to pass. OTel avoids this by injecting through a propagator-driven commenter after the span is established (R2dbcSqlCommenterUtil). A shippable version needs the traceparent wired in (which depends on the hook-point/lifecycle change above).
| if (INJECT_COMMENT) { | ||
| sql = R2dbcSQLCommenter.inject(sql, null, dbType, null, null, null); | ||
| } | ||
| return R2dbcConnectionInfo.of(originalSql, dbType, null, null, null); |
There was a problem hiding this comment.
Dead connection-metadata fields (consequence of the hook point). R2dbcConnectionInfo.of(originalSql, dbType, null, null, null) passes null for dbInstance/dbUser/dbHostname — so those fields exist on R2dbcConnectionInfo but are never populated, and the decorator overrides that would consume them (R2dbcDecorator.dbUser/dbInstance/dbHostname, lines 58-69) all return null. Net effect on spans: db.name (REQUIRED), peer.hostname, db.user, network.destination.port are never set. OTel populates all of these from ConnectionFactoryOptions (R2dbcSqlAttributesGetter.java#L50-L116). These three comments are one finding with one fix: move the hook to the connection factory.
| @Advice.OnMethodEnter(suppress = Throwable.class) | ||
| public static AgentScope onEnter() { | ||
| AgentSpan span = startSpan("r2dbc", R2DBC_BATCH); | ||
| DECORATE.afterStart(span); |
There was a problem hiding this comment.
Batch spans carry zero connection metadata (secondary). This advice does startSpan + afterStart but never calls onConnection/attaches any R2dbcConnectionInfo, so Batch.execute() spans have no db.*/peer.* tags at all — even less than the statement path. OTel treats batch executions through the same per-execution DbExecution carrying ConnectionFactoryOptions, so batch spans get the same connection attributes as statement spans. Same root fix (connection-factory hook) resolves this too. Lower priority than the statement path, but noting it for completeness of the shippability assessment.
skill(apm-integrations): sharpen SPI + muzzle rules for database category, add eager-connect idiom From the database eval cycle (reference PRs #11996/#11997/#12032). Most database findings turned out to be adherence gaps against rules that already exist, not missing rules — so this sharpens the existing rules with the concrete failure modes, plus adds one genuinely-new idiom. instrumenter-module.md: - SPI rule: the ForTypeHierarchy exception now explicitly covers being handed a CONCRETE driver that implements a JDK SPI (e.g. org.postgresql.jdbc.PgStatement implements java.sql.Statement), not just interface-only spec jars. The old wording only triggered on "interface-only jar", so an agent given a single concrete driver didn't apply it — the PostgreSQL regen (R-DB-2) fell into exactly this trap and shipped a concrete-class module that also collides at runtime with the existing jdbc/ SPI module. - New: database clients must populate connection metadata eagerly at connect/factory time (JDBC DriverInstrumentation; R2DBC ConnectionFactoryOptions), not lazily per query (R-DB-3). muzzle.md: - assertInverse rule reinforced with the concrete-driver failure mode (R-DB-4): a pinned dependency version is not an API-shape boundary. Draft — will be refined as feedback comes in from the database reference PR reviews. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> skill(apm-integrations): couple module placement to a taken super() name (R-DB-1) Database category gap sweep (2026-08-05) confirmed R-DB-1 is category-wide, not a Cassandra quirk: any library with a version-sibling family directory whose name differs from the integration slug will collide. The existing "modify in place, don't create a parallel module" rule (line 47) doesn't cover the case the Cassandra regen actually hit: - eval slug `cassandra` != family dir `datastax-cassandra/` - surviving siblings (datastax-cassandra-3.0/-3.8) already declare super("cassandra") - under the blind protocol the same-version (4.0) module was DELETED, so "modify it in place" had no target — but the name was still taken The agent created a new top-level instrumentation/cassandra/ module with a duplicate super("cassandra") registration -> silent tracing outage (advice never applied, zero spans, tests timed out, no build error). Fix: grep the tree for the intended super() name BEFORE creating a module; if any module (including untouched version-siblings) holds it, join that family directory rather than minting a new top-level slug. Placement and name are one decision: a taken name dictates the directory. If there is no collision-free home, STOP and surface it. Verified against #12114's existing commit (37e661c): the SPI-collision case (R-DB-2) and eager-connect (R-DB-3) are already covered there; this is the distinct version-sibling-family placement case they don't address. Other sweep findings routed elsewhere (N-DBM-2 silent-downscope -> toolkit prompt; reviewer-check candidates -> toolkit repo), not this skill PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> skill(apm-integrations): cross-ref DB-client design rules to category guide Points instrumenter-module.md at the toolkit categories/database.md decision block (force-read at target-selection) for db.instance sourcing, wrapper pattern, DBM gating, and eager connect metadata (R-DB-3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> skill(apm-integrations): fix dangling toolkit cross-ref + retain lazy DBInfo fallback (review #12114) Addresses @dougqh's review: the DB-client design-rules bullet pointed at the toolkit file `categories/database.md` "force-read at target-selection" — but that file is not in this repo and "target-selection" is not a named SKILL.md step, so an agent reading the skill hit a dangling pointer. Reworded to: - name the real steps where the rules apply (Step 3 target selection / Step 5 module write); - state plainly that these are the human-readable rules, additionally enforced as force-read toolkit prompt blocks (apm-instrumentation-toolkit#580) when driven by the toolkit — and that the toolkit file is not part of this repo; - fold in the Codex/master-accurate note to retain a lazy parseDBInfo fallback for connect paths the eager Driver.connect hook doesn't cover (DataSource/proxy), not eager-only. No rule content removed; wording only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> skill(apm-integrations): fold 3 bot-review refinements — family-name evidence, concrete-hook scope, R2DBC create() (review #12114) Addresses valid Codex/Datadog-Autotest findings on #12114 (all confirmed against repo facts): - R-DB-1: "taken super() name dictates the directory" was too absolute. Reworded to treat name matches as EVIDENCE, then confirm by coordinates/packages/muzzle. Shared config names (jax-rs across rs/jersey/resteasy; ci-visibility across nine) do NOT identify one family → place by target library; if ambiguous, STOP. Also corrected the mechanism note: equal super() names do not themselves cause a registration outage (InstrumenterIndex indexes by module class); the real harm is a duplicate same-version module (the Cassandra case, unchanged). - R-DB-2: scoped the concrete-driver prohibition to BEHAVIORALLY-REDUNDANT advice. Vendor-only lifecycle/compat hooks not declared on the SPI legitimately need a concrete module (DBMCompatibleConnectionInstrumentation, DB2 JDBC, Tomcat Request.recycle()). Reject a concrete hook only when its method is already advised via the SPI. - R2DBC: corrected the factual claim that ConnectionFactoryOptions is available at ConnectionFactory.create(). create() is zero-arg returning a Publisher; options must be captured earlier at ConnectionFactories.get(options) into a ContextStore<ConnectionFactory,Options> and threaded onto the async-emitted Connection. Wording/accuracy only; no gating rule reversed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> skill(apm-integrations): R-DB-5 must keep the connection/session default, override only when richer (review #12114) Addresses Datadog Autotest :23 (valid): "derive db.instance from the operation not the connection default" read as "drop the default", which would lose db.instance/keyspace for operations with no result metadata (e.g. a Cassandra write). Reworded to the correct two-phase framing — keep the connection/session value as the DEFAULT, override per-operation only when the operation supplies a more specific value (fully-qualified other_ks.table, or keyspace from response ColumnDefinitions). This matches the toolkit R-DB-5 rule (session default + onResponse override), which was already correct; only the skill one-liner was imprecise. The other three Autotest :12/:59/:121 findings this run are re-scans already fixed by the prior two commits (concrete-hook scoping, name-as-evidence, R2DBC create() zero-arg). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> skill(apm-integrations): broaden super() name search + fix Cassandra outage causal claim (review #12114) Two valid Datadog Autotest catches (15:07 run): - :52 — the name-search grep matched only the FIRST super(...) arg, missing later args (super("vertx","vertx-sql-client")) and names held in constants. Broadened to grep all super(...) args and to also scan module classes / name constants when a literal grep misses. - :59 — the Cassandra "Concrete failure" paragraph attributed the tracing outage to two same-name @autoservice registrations, but modules are indexed by class name (equal names are legal). Reframed: the mechanism is a DUPLICATE module advising the same types (mutual suppression via the shared call-depth guard); the shared super("cassandra") name is a symptom, not the cause. Now consistent with the note added earlier. The third finding this run (:23 db.instance keep-the-default) is a stale re-scan already fixed in 04bfe52. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> skill(apm-integrations): make DB rules standalone — remove toolkit references (review #12114) Per architectural rule: the skill must be usable standalone; dependency direction is one-way (toolkit references the skill, never the reverse). The prior wording pointed the reader at the apm-instrumentation-toolkit (categories/database.md, PR #580, "when driven by the toolkit … force-read prompt blocks"). Removed all of it — the database-client design rules now stand on their own with no toolkit dependency. Also dropped the (R-DB-3) eval-bookkeeping tag, which is meaningless to a standalone skill reader. Rule content unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Merge branch 'master' into skill/database-category-rules-20260730 skill(apm-integrations): muzzle result is not proof a matcher target exists (review #12114) Addresses Codex P2 on muzzle.md: the prior text implied a muzzle pass verified the concrete matcher target (PgStatement) exists on old versions. Muzzle derives references from advice bytecode + explicit additional references, NOT from instrumentedType()/named(...) matcher strings, so a matcher-only concrete class is a blind spot. Reworded to: do not infer matcher-target presence/absence from a muzzle result; back version-dependent matcher rules with an explicit muzzle reference or a runtime/latest-dep test. Also folded in the PostgreSQL jdbc2/3/4 vs PgPreparedStatement nuance and dropped the imprecise "unchanged back through 9.2" claim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> skill(apm-integrations): make the family-placement example repo-generic (standalone) The skill must be readable by any agent working on dd-trace-java integrations, independent of the eval toolkit. Reworded the Cassandra family-placement example to drop eval-specific framing ("regen", "R-DB-1", "the eval was given the slug", "blind protocol") and state it as a plain worked example. Same lesson: a name taken by an existing family means join that family, not create a parallel module; the harm is duplicate advice on the same types. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
What
Reference PR from the apm-instrumentation-toolkit's net-new generation workflow, targeting
io.r2dbc:r2dbc-spi:1.0.0.RELEASE. No dd-trace-java module exists for R2DBC today, so this was not a blind regen — there was nothing to hide/restore. This measures from-scratch generation quality against an SPI-only Maven coordinate (no concrete driver dependency).Generated module:
dd-java-agent/instrumentation/r2dbc/r2dbc-1.0/Toolkit reviewer verdict:
needs_workThe toolkit's own review_cycle ran 5 iterations and did not reach
approved(todos_remaining=15after iter5, the apparent iteration cap). Total cost: $65.81, duration: ~2h17m.What went right (structural checks all clean):
io.r2dbc.spi.Statement/Connection/Batch), no concrete-driver couplingsuper("r2dbc"), no spurious version aliasWhat's broken — real semantic-completeness gaps (dominant finding):
db.name(REQUIRED tag) is never set.ConnectionInstrumentationonly extracts fromConnectionMetadata, which doesn't expose database/host/port/user. Root cause (per reviewer): should hookConnectionFactory.create()or theConnectionconstructor instead, which exposesConnectionFactoryOptionswith that data.peer.hostname,db.user,network.destination.port(RECOMMENDED tags) — all missing for the same reason.R2dbcConnectionInfohas fields (dbInstance,dbUser,dbHostname) defined but never populated.BatchInstrumentationspans have zero connection metadata tags.db.typevalue between batch ("r2dbc") and statement ("testdb") tests.Minor: 1 inline narrative comment to remove; missing explicit muzzle fail blocks for 0.8.x/0.9.x version families. Originally noted as "not urgent —
assertInverse=truealready isolates," but see CI Triage Status below: thatassertInverse=truewas actually asserting a false compatibility boundary and has been removed as part of a CI fix, so there is now no automated check that pre-1.0 releases are excluded — this remains a real (if low-priority) gap.This PR is left as-is (not iterated further) so the diff reflects exactly what the toolkit produced — do not fix findings on this branch; it's a research artifact, not a merge candidate.
Comparison with OpenTelemetry's R2DBC instrumentation (reference for the correct hook point)
OTel Java solved exactly the gap above by hooking a different SPI entry point. R2DBC exposes connection identity (host/port/db/user) only in
io.r2dbc.spi.ConnectionFactoryOptionsat connection-factory creation — not onConnection/ConnectionMetadata(which exposes onlygetDatabaseProductName()/getDatabaseVersion()). Where you hook decides whether you can populate these tags at all.Connection.createStatement()+connection.getMetadata()(ConnectionInstrumentation.java:30,38,53)ConnectionFactories.find(ConnectionFactoryOptions)(instrumentation/r2dbc-1.0/javaagent/.../R2dbcInstrumentation.java)R2dbcConnectionInfoDbExecutionholding capturedConnectionFactoryOptions(.../library/.../internal/DbExecution.java)db.name/db.namespaceConnectionMetadatalacks itConnectionFactoryOptions.DATABASEpeer.hostname/server.addressConnectionFactoryOptions.HOSTConnectionFactoryOptions.PORTdb.userConnectionFactoryOptions.USERtraceparent(full mode)traceParent=null(ConnectionInstrumentation.java:64)OTel history check: no PR/issue in
opentelemetry-java-instrumentation'sr2dbc-1.0shows these attributes were ever missing-then-added — the factory-level hook is original design, chosen precisely because it's the one place the full connection params are structured data. So this isn't a shared gap; it's a design divergence.Takeaway: the toolkit correctly recognized R2DBC as SPI-shaped and hooked
io.r2dbc.spi.*interfaces — but hooked the wrong SPI entry point for connection identity. The remediation is to captureConnectionFactoryOptionsat factory-create time and thread it forward (as OTel'sDbExecutiondoes), instead of readingConnectionMetadataper statement. Full write-up: toolkit repodocs/eval-research/r2dbc-otel-comparison.md.CI Triage Status
Last updated: 2026-07-22
Confirmed research findings (do not fix — core instrumentation-logic gap):
db.name(REQUIRED tag),peer.hostname/db.user/network.destination.port(RECOMMENDED tags) never populated.ConnectionInstrumentationonly extracts fromConnectionMetadata(product name/version only) instead of hookingConnectionFactory.create()/theConnectionconstructor, which exposesConnectionFactoryOptionswith the actual host/port/db/user data. No CI check catches this — there's no assertion anywhere that these tags must be present — so this finding would otherwise only live in prose. See "What's broken" above for full detail; candidate skill fix is N-DBM-2 (silent-narrowing process gap) in the toolkit'sdocs/eval-research/BACKLOG.md.BatchInstrumentationspans carry zero connection-metadata tags — same root cause as above, compounded (batch path never wires through what statement path partially has).R2dbcConnectionInfofields (dbInstance,dbUser,dbHostname) defined but never populated — same root cause.Fixed (scaffolding, root cause is a documented finding):
dd-gitlab/muzzle: [4/8]—muzzle-AssertFail-io.r2dbc-r2dbc-spi-0.8.6.RELEASEand-0.9.1.RELEASEFAILED: "Instrumentation unexpectedly passed Muzzle validation". The generatedbuild.gradledeclaredversions = "[1.0.0.RELEASE,)"withassertInverse = true, asserting pre-1.0 releases must fail — butio.r2dbc.spi.Statement/Connection/Batchare structurally unchanged across 0.8.x/0.9.x/1.0.x, so they pass anyway. Fixed in commit521b0d52a1: droppedassertInverse = true. Verified locally AND confirmed green in CI (allmuzzle: [*/8]shards SUCCESS post-push). Does not touch the hook point, tags, or integration name — the dominant finding above (missingdb.name/peer.hostname/db.user/etc.) is unrelated and unaddressed here on purpose.Fixed (config/mechanical):
dd-gitlab/build—verifyAgentJarIntegrationsfailed: "Integration list differs frommetadata/agent-jar-checks.properties" —super("r2dbc")registered but the golden file was never updated. Fixed in commit368d2094ce: addedr2dbc,\alphabetically betweenquartzandratpack. Verified locally AND confirmed green in CI post-push.Classified as flake / unrelated:
Still unclassified: none.
Process note (not a code finding):
dd-gitlab/validate_supported_configurations_v2_local_file— Same pattern as PR [reference] eval: blind regeneration of PostgreSQL JDBC driver (toolkit output) #11997's PostgreSQL finding, investigated the same way: manually checked https://feature-parity.us1.prod.dog/#/configurations forDD_TRACE_R2DBC_ENABLED/DD_TRACE_R2DBC_ANALYTICS_ENABLED/DD_TRACE_R2DBC_ANALYTICS_SAMPLE_RATE— none visible in the registry UI, consistent with these being genuinely new config names with no existing registry entry. Our local entries follow the same convention as master's real integrations. Confirmed process note — remediation requires feature-parity registry write access neither of us has, not add-trace-javacommit.🤖 Generated by apm-instrumentation-toolkit. Not a merge candidate — reference/research artifact only.