Skip to content

Commit a0acab9

Browse files
deejgregormcculls
authored andcommitted
Cleanup Hikari instrumentation
- Comment on the overall structure and highlight related instrumentations. - Avoid using reflection and breakup HikariConcurrentBagInstrumentation.ConstructorAdvice into separate, simpler instrumentations: HikariPoolInstrumentation and HikariConcurrentBagHandoffQueueInstrumentation. - Move HikariBlockedTrackingSynchronousQueue into HikariConcurrentBagHandoffQueueInstrumentation. - Make sure the instrumentationName is consistently "jdbc".
1 parent 3279725 commit a0acab9

File tree

6 files changed

+170
-65
lines changed

6 files changed

+170
-65
lines changed

dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/HikariBlockedTracker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package datadog.trace.instrumentation.jdbc;
22

3-
/** Shared blocked getConnection() tracking ThreadLocking for Hikari. */
3+
/** Shared blocked getConnection() tracking {@link ThreadLocal} for Hikari. */
44
public class HikariBlockedTracker {
55
private static final ThreadLocal<Boolean> tracker = ThreadLocal.withInitial(() -> false);
66

dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/HikariBlockedTrackingSynchronousQueue.java

Lines changed: 0 additions & 18 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package datadog.trace.instrumentation.jdbc;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers.declaresField;
4+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
5+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
6+
7+
import com.google.auto.service.AutoService;
8+
import datadog.trace.agent.tooling.Instrumenter;
9+
import datadog.trace.agent.tooling.InstrumenterModule;
10+
import datadog.trace.api.InstrumenterConfig;
11+
import java.util.concurrent.SynchronousQueue;
12+
import java.util.concurrent.TimeUnit;
13+
import net.bytebuddy.asm.Advice;
14+
import net.bytebuddy.description.type.TypeDescription;
15+
import net.bytebuddy.matcher.ElementMatcher;
16+
17+
/**
18+
* Detect blocking for newer Hikari versions starting with commit f0b3c520c (>=2.6.0) by looking for
19+
* calls to <code>handoffQueue.poll(timeout, NANOSECONDS)</code>.
20+
*/
21+
@AutoService(InstrumenterModule.class)
22+
public final class HikariConcurrentBagHandoffQueueInstrumentation extends InstrumenterModule.Tracing
23+
implements Instrumenter.ForSingleType,
24+
Instrumenter.HasMethodAdvice,
25+
Instrumenter.WithTypeStructure {
26+
27+
public HikariConcurrentBagHandoffQueueInstrumentation() {
28+
super("jdbc");
29+
}
30+
31+
@Override
32+
protected boolean defaultEnabled() {
33+
return InstrumenterConfig.get().isJdbcPoolWaitingEnabled();
34+
}
35+
36+
@Override
37+
public String instrumentedType() {
38+
return "com.zaxxer.hikari.util.ConcurrentBag";
39+
}
40+
41+
@Override
42+
public ElementMatcher<TypeDescription> structureMatcher() {
43+
return declaresField(named("handoffQueue"));
44+
}
45+
46+
@Override
47+
public String[] helperClassNames() {
48+
return new String[] {
49+
packageName + ".HikariBlockedTracker",
50+
packageName
51+
+ ".HikariConcurrentBagHandoffQueueInstrumentation$BlockedTrackingSynchronousQueue",
52+
};
53+
}
54+
55+
@Override
56+
public void methodAdvice(MethodTransformer transformer) {
57+
transformer.applyAdvice(
58+
isConstructor(),
59+
HikariConcurrentBagHandoffQueueInstrumentation.class.getName() + "$ConstructorAdvice");
60+
}
61+
62+
public static class ConstructorAdvice {
63+
@Advice.OnMethodExit(suppress = Throwable.class)
64+
static void after(
65+
@Advice.FieldValue(value = "handoffQueue", readOnly = false)
66+
SynchronousQueue handoffQueue) {
67+
handoffQueue = new BlockedTrackingSynchronousQueue<>();
68+
}
69+
}
70+
71+
public static class BlockedTrackingSynchronousQueue<T> extends SynchronousQueue<T> {
72+
public BlockedTrackingSynchronousQueue() {
73+
// This assumes the initialization of the SynchronousQueue in ConcurrentBag doesn't change
74+
super(true);
75+
}
76+
77+
@Override
78+
public T poll(long timeout, TimeUnit unit) throws InterruptedException {
79+
HikariBlockedTracker.setBlocked();
80+
return super.poll(timeout, unit);
81+
}
82+
}
83+
}

dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/HikariConcurrentBagInstrumentation.java

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,32 @@
66
import static datadog.trace.instrumentation.jdbc.PoolWaitingDecorator.DECORATE;
77
import static datadog.trace.instrumentation.jdbc.PoolWaitingDecorator.POOL_WAITING;
88
import static java.util.Collections.singletonMap;
9-
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
109

1110
import com.google.auto.service.AutoService;
12-
import com.zaxxer.hikari.pool.HikariPool;
1311
import com.zaxxer.hikari.util.ConcurrentBag;
1412
import datadog.trace.agent.tooling.Instrumenter;
1513
import datadog.trace.agent.tooling.InstrumenterModule;
1614
import datadog.trace.api.InstrumenterConfig;
1715
import datadog.trace.bootstrap.InstrumentationContext;
1816
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
19-
import java.lang.reflect.Field;
2017
import java.util.Map;
2118
import java.util.concurrent.TimeUnit;
2219
import net.bytebuddy.asm.Advice;
2320

2421
/**
2522
* Instrument Hikari's ConcurrentBag class to detect when blocking occurs trying to get an entry
26-
* from the connection pool.
23+
* from the connection pool. There are two related instrumentations to detect blocking for different
24+
* versions of Hikari. The {@link HikariBlockedTracker} contextStore is used to pass blocking state
25+
* from the other instrumentations to this class.
26+
*
27+
* <ul>
28+
* <li>Before commit f0b3c520c (2.4.0 <= version < 2.6.0): calls to <code>
29+
* synchronizer.waitUntilSequenceExceeded(startSeq, timeout)</code> with {@link
30+
* HikariQueuedSequenceSynchronizerInstrumentation}
31+
* <li>Commit f0b3c520c and later (version >= 2.6.0): calls to <code>
32+
* handoffQueue.poll(timeout, NANOSECONDS)</code> with {@link
33+
* HikariQueuedSequenceSynchronizerInstrumentation}
34+
* </ul>
2735
*/
2836
@AutoService(InstrumenterModule.class)
2937
public final class HikariConcurrentBagInstrumentation extends InstrumenterModule.Tracing
@@ -46,60 +54,26 @@ public String instrumentedType() {
4654
@Override
4755
public String[] helperClassNames() {
4856
return new String[] {
49-
packageName + ".HikariBlockedTrackingSynchronousQueue",
50-
packageName + ".HikariBlockedTracker",
51-
packageName + ".PoolWaitingDecorator"
57+
packageName + ".HikariBlockedTracker", packageName + ".PoolWaitingDecorator"
5258
};
5359
}
5460

5561
@Override
5662
public Map<String, String> contextStore() {
57-
// For getting the poolName
63+
// The contextStore Map is populated by HikariPoolInstrumentation
5864
return singletonMap("com.zaxxer.hikari.util.ConcurrentBag", String.class.getName());
5965
}
6066

6167
@Override
6268
public void methodAdvice(MethodTransformer transformer) {
63-
transformer.applyAdvice(
64-
isConstructor(), HikariConcurrentBagInstrumentation.class.getName() + "$ConstructorAdvice");
6569
transformer.applyAdvice(
6670
named("borrow"), HikariConcurrentBagInstrumentation.class.getName() + "$BorrowAdvice");
6771
}
6872

69-
public static class ConstructorAdvice {
70-
@Advice.OnMethodExit(suppress = Throwable.class)
71-
static void after(@Advice.This ConcurrentBag<?> thiz)
72-
throws IllegalAccessException, NoSuchFieldException {
73-
try {
74-
Field handoffQueueField = thiz.getClass().getDeclaredField("handoffQueue");
75-
handoffQueueField.setAccessible(true);
76-
handoffQueueField.set(thiz, new HikariBlockedTrackingSynchronousQueue<>());
77-
} catch (NoSuchFieldException e) {
78-
// ignore -- see HikariQueuedSequenceSynchronizerInstrumentation for older Hikari versions
79-
}
80-
81-
Field hikariPoolField = thiz.getClass().getDeclaredField("listener");
82-
hikariPoolField.setAccessible(true);
83-
HikariPool hikariPool = (HikariPool) hikariPoolField.get(thiz);
84-
85-
/*
86-
* In earlier versions of Hikari, poolName is directly inside HikariPool, and
87-
* in later versions it is in the PoolBase superclass.
88-
*/
89-
final Class<?> hikariPoolSuper = hikariPool.getClass().getSuperclass();
90-
final Class<?> poolNameContainingClass;
91-
if (!hikariPoolSuper.getName().equals("java.lang.Object")) {
92-
poolNameContainingClass = hikariPoolSuper;
93-
} else {
94-
poolNameContainingClass = hikariPool.getClass();
95-
}
96-
Field poolNameField = poolNameContainingClass.getDeclaredField("poolName");
97-
poolNameField.setAccessible(true);
98-
String poolName = (String) poolNameField.get(hikariPool);
99-
InstrumentationContext.get(ConcurrentBag.class, String.class).put(thiz, poolName);
100-
}
101-
}
102-
73+
/**
74+
* Instead of always starting and ending a span, a pool.waiting span is only created if blocking
75+
* is detected when attempting to get a connection from the pool.
76+
*/
10377
public static class BorrowAdvice {
10478
@Advice.OnMethodEnter(suppress = Throwable.class)
10579
public static Long onEnter() {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package datadog.trace.instrumentation.jdbc;
2+
3+
import static java.util.Collections.singletonMap;
4+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
5+
6+
import com.google.auto.service.AutoService;
7+
import com.zaxxer.hikari.util.ConcurrentBag;
8+
import datadog.trace.agent.tooling.Instrumenter;
9+
import datadog.trace.agent.tooling.InstrumenterModule;
10+
import datadog.trace.api.InstrumenterConfig;
11+
import datadog.trace.bootstrap.InstrumentationContext;
12+
import java.util.Map;
13+
import net.bytebuddy.asm.Advice;
14+
15+
/**
16+
* Store the poolName associated with a {@link ConcurrentBag} for later use in {@link
17+
* HikariConcurrentBagInstrumentation}.
18+
*/
19+
@AutoService(InstrumenterModule.class)
20+
public final class HikariPoolInstrumentation extends InstrumenterModule.Tracing
21+
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
22+
23+
public HikariPoolInstrumentation() {
24+
super("jdbc");
25+
}
26+
27+
@Override
28+
protected boolean defaultEnabled() {
29+
return InstrumenterConfig.get().isJdbcPoolWaitingEnabled();
30+
}
31+
32+
@Override
33+
public String instrumentedType() {
34+
return "com.zaxxer.hikari.pool.HikariPool";
35+
}
36+
37+
@Override
38+
public Map<String, String> contextStore() {
39+
return singletonMap("com.zaxxer.hikari.util.ConcurrentBag", String.class.getName());
40+
}
41+
42+
@Override
43+
public void methodAdvice(MethodTransformer transformer) {
44+
transformer.applyAdvice(
45+
isConstructor(), HikariPoolInstrumentation.class.getName() + "$ConstructorAdvice");
46+
}
47+
48+
public static class ConstructorAdvice {
49+
@Advice.OnMethodExit(suppress = Throwable.class)
50+
static void after(
51+
@Advice.FieldValue("connectionBag") ConcurrentBag concurrentBag,
52+
@Advice.FieldValue("poolName") String poolName) {
53+
InstrumentationContext.get(ConcurrentBag.class, String.class).put(concurrentBag, poolName);
54+
}
55+
}
56+
}

dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/HikariQueuedSequenceSynchronizerInstrumentation.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,31 @@
77
import datadog.trace.agent.tooling.InstrumenterModule;
88
import net.bytebuddy.asm.Advice;
99

10-
/** Blocked getConnection() tracking for Hikari starting before commit f0b3c520c. */
10+
/**
11+
* Detect blocking for older Hikari versions before commit f0b3c520c (<2.6.0) by looking for calls
12+
* to <code>synchronizer.waitUntilSequenceExceeded(startSeq, timeout)</code>.
13+
*/
1114
@AutoService(InstrumenterModule.class)
1215
public final class HikariQueuedSequenceSynchronizerInstrumentation
1316
extends InstrumenterModule.Tracing
1417
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
1518

1619
public HikariQueuedSequenceSynchronizerInstrumentation() {
17-
super("jdbc-datasource");
20+
super("jdbc");
1821
}
1922

2023
@Override
2124
public String instrumentedType() {
2225
return "com.zaxxer.hikari.util.QueuedSequenceSynchronizer";
2326
}
2427

28+
@Override
29+
public String[] helperClassNames() {
30+
return new String[] {
31+
packageName + ".HikariBlockedTracker",
32+
};
33+
}
34+
2535
@Override
2636
public void methodAdvice(MethodTransformer transformer) {
2737
transformer.applyAdvice(

0 commit comments

Comments
 (0)