Skip to content

Commit 1344a77

Browse files
committed
Restore CommandSchedulerInstrumentation for v5.0
Restores context propagation through the Vert.x SQL client pool command dispatch pipeline. This is needed for JDBCPool which bypasses Pool.pool() factory: the null-connectOptions check correctly suppresses the vertx-sql-client span, but downstream JDBC instrumentation needs the correct parent context to create its own span. CommandSchedulerInstrumentation stores the context on the first schedule() call and restores it when the pool dispatches to a connection, ensuring executeBlocking on worker threads sees the right parent. With the PoolInstrumentation typeMatcher now fixed, this is safe for non-JDBC pools: the vertx-sql-client CLIENT span is already created before CommandScheduler fires, so the context propagation is harmless.
1 parent fd9229e commit 1344a77

2 files changed

Lines changed: 82 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.vertx.v5_0.sql;
7+
8+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
9+
import static net.bytebuddy.matcher.ElementMatchers.named;
10+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
11+
12+
import io.opentelemetry.context.Context;
13+
import io.opentelemetry.context.Scope;
14+
import io.opentelemetry.instrumentation.api.util.VirtualField;
15+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
16+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
17+
import io.vertx.sqlclient.internal.command.CommandBase;
18+
import javax.annotation.Nullable;
19+
import net.bytebuddy.asm.Advice;
20+
import net.bytebuddy.description.type.TypeDescription;
21+
import net.bytebuddy.matcher.ElementMatcher;
22+
23+
/**
24+
* Propagates OpenTelemetry context through the Vert.x SQL client connection pool.
25+
*
26+
* <p>When a query is initiated (e.g. {@code pool.query("SELECT ...").execute()}), the correct
27+
* OpenTelemetry context is active on the event loop thread. The pool may queue the request and
28+
* dispatch it later when a connection becomes available — by which time the event loop may be
29+
* handling a different request with a different context.
30+
*
31+
* <p>This instrumentation captures the context on the first {@code CommandScheduler.schedule()}
32+
* call (from the query executor, with the correct context) and restores it on subsequent calls
33+
* (from the pool to the connection, where the context may be stale). This ensures that downstream
34+
* instrumentation (e.g. JDBC) on worker threads sees the correct parent context.
35+
*/
36+
public class CommandSchedulerInstrumentation implements TypeInstrumentation {
37+
38+
@Override
39+
public ElementMatcher<TypeDescription> typeMatcher() {
40+
return implementsInterface(named("io.vertx.sqlclient.internal.command.CommandScheduler"));
41+
}
42+
43+
@Override
44+
public void transform(TypeTransformer transformer) {
45+
transformer.applyAdviceToMethod(
46+
named("schedule")
47+
.and(takesArgument(0, named("io.vertx.sqlclient.internal.command.CommandBase")))
48+
.and(takesArgument(1, named("io.vertx.core.Completable"))),
49+
CommandSchedulerInstrumentation.class.getName() + "$ScheduleAdvice");
50+
}
51+
52+
// VirtualField.find requires the raw type; CommandBase is invoked reflectively by ByteBuddy
53+
@SuppressWarnings({"unused", "rawtypes"})
54+
public static class ScheduleAdvice {
55+
56+
@Advice.OnMethodEnter(suppress = Throwable.class)
57+
@Nullable
58+
public static Scope onEnter(@Advice.Argument(0) CommandBase<?> command) {
59+
VirtualField<CommandBase, Context> contextField =
60+
VirtualField.find(CommandBase.class, Context.class);
61+
Context stored = contextField.get(command);
62+
if (stored == null) {
63+
// First schedule call (query executor → pool or direct connection).
64+
// The current OpenTelemetry context is correct — store it on the command.
65+
contextField.set(command, Context.current());
66+
return null;
67+
}
68+
// Subsequent schedule call (pool → connection).
69+
// Restore the stored context so that executeBlocking dispatches with
70+
// the correct parent for downstream instrumentation (e.g. JDBC).
71+
return stored.makeCurrent();
72+
}
73+
74+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
75+
public static void onExit(@Advice.Enter @Nullable Scope scope) {
76+
if (scope != null) {
77+
scope.close();
78+
}
79+
}
80+
}
81+
}

instrumentation/vertx/vertx-sql-client/vertx-sql-client-5.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v5_0/sql/VertxSqlClientInstrumentationModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public List<String> injectedClassNames() {
4343
@Override
4444
public List<TypeInstrumentation> typeInstrumentations() {
4545
return asList(
46+
new CommandSchedulerInstrumentation(),
4647
new DriverInstrumentation(),
4748
new PoolInstrumentation(),
4849
new SqlClientBaseInstrumentation(),

0 commit comments

Comments
 (0)