Skip to content

Commit df910b9

Browse files
committed
Apache DBCP2: Add a span when waiting on the pool
1 parent b673261 commit df910b9

File tree

5 files changed

+99
-15
lines changed

5 files changed

+99
-15
lines changed

dd-java-agent/instrumentation/jdbc/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies {
2727
testImplementation group: 'com.h2database', name: 'h2', version: '[1.3.168,1.3.169]'// first jdk 1.6 compatible
2828
testImplementation group: 'org.apache.derby', name: 'derby', version: '10.6.1.0'
2929
testImplementation group: 'org.hsqldb', name: 'hsqldb', version: '2.0.0'
30+
testImplementation group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.10.0'
3031

3132
testImplementation group: 'org.apache.tomcat', name: 'tomcat-jdbc', version: '7.0.19'
3233
// tomcat needs this to run
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package datadog.trace.instrumentation.jdbc;
2+
3+
import com.google.auto.service.*;
4+
import datadog.trace.agent.tooling.*;
5+
import datadog.trace.bootstrap.instrumentation.api.*;
6+
import net.bytebuddy.asm.*;
7+
import org.slf4j.*;
8+
9+
import java.util.concurrent.*;
10+
11+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.*;
12+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
13+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
14+
15+
@AutoService(InstrumenterModule.class)
16+
public final class Dbcp2LinkedBlockingDequeInstrumentation extends InstrumenterModule.Tracing
17+
implements Instrumenter.ForKnownTypes, Instrumenter.HasMethodAdvice {
18+
19+
private static final Logger log = LoggerFactory.getLogger(Dbcp2LinkedBlockingDequeInstrumentation.class);
20+
21+
public Dbcp2LinkedBlockingDequeInstrumentation() {
22+
super("jdbc-datasource");
23+
}
24+
25+
@Override
26+
public String[] knownMatchingTypes() {
27+
return new String[] {
28+
"org.apache.commons.pool2.impl.LinkedBlockingDeque", // standalone
29+
"org.apache.tomcat.dbcp.pool2.impl.LinkedBlockingDeque" // bundled with Tomcat
30+
};
31+
}
32+
33+
@Override
34+
public void methodAdvice(MethodTransformer transformer) {
35+
transformer.applyAdvice(
36+
named("pollFirst")
37+
.and(takesArguments(1)),
38+
Dbcp2LinkedBlockingDequeInstrumentation.class.getName() + "$PollFirstAdvice");
39+
}
40+
41+
public static class PollFirstAdvice {
42+
private static final String POOL_WAITING = "pool.waiting";
43+
44+
@Advice.OnMethodEnter(suppress = Throwable.class)
45+
public static AgentSpan onEnter() {
46+
return startSpan("dbcp2", POOL_WAITING);
47+
}
48+
49+
@Advice.OnMethodExit(suppress = Throwable.class)
50+
public static void onExit(@Advice.Enter final AgentSpan span) {
51+
span.finish();
52+
}
53+
}
54+
}

dd-java-agent/instrumentation/jdbc/src/test/groovy/SaturatedPoolBlockingTest.groovy

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import datadog.trace.agent.test.AgentTestRunner
22
import com.zaxxer.hikari.HikariConfig
33
import com.zaxxer.hikari.HikariDataSource
4+
import org.apache.commons.dbcp2.BasicDataSource
45
import test.TestDataSource
6+
import test.TestDriver
57

8+
import javax.sql.DataSource
9+
import java.sql.SQLException
610
import java.sql.SQLTimeoutException
711
import java.sql.SQLTransientConnectionException
12+
import java.time.Duration
813

914
/**
1015
* Ideas taken from Hikari's com.zaxxer.hikari.pool.TestSaturatedPool830.
1116
*/
1217
class SaturatedPoolBlockingTest extends AgentTestRunner {
13-
def "saturated pool test"(int connectionTimeout, Long exhaustPoolForMillis, int expectedWaitingSpans, boolean expectedTimeout) {
18+
public static final int CONNECTION_TIMEOUT = 1000
19+
20+
def "saturated pool test"(Closure<DataSource> createDataSource, Long exhaustPoolForMillis, int expectedWaitingSpans, boolean expectedTimeout) {
1421
setup:
1522
TEST_WRITER.setFilter((trace) -> trace.get(0).getOperationName() == "test.when")
16-
17-
final HikariConfig config = new HikariConfig()
18-
config.setPoolName("testPool")
19-
config.setMaximumPoolSize(1)
20-
config.setConnectionTimeout(connectionTimeout)
21-
config.setDataSourceClassName(TestDataSource.class.getName())
22-
final HikariDataSource ds = new HikariDataSource(config)
23+
final DataSource ds = createDataSource()
2324

2425
when:
2526
if (exhaustPoolForMillis != null) {
@@ -43,6 +44,12 @@ class SaturatedPoolBlockingTest extends AgentTestRunner {
4344
}
4445
} catch (SQLTimeoutException ignored) { // Hikari, older
4546
timedOut = true
47+
} catch (SQLException e) {
48+
if (e.getMessage().contains("pool error Timeout waiting for idle object")) { // dbcp2
49+
timedOut = true
50+
} else {
51+
throw e
52+
}
4653
}
4754
span.finish()
4855

@@ -59,10 +66,32 @@ class SaturatedPoolBlockingTest extends AgentTestRunner {
5966
}
6067

6168
where:
62-
connectionTimeout | exhaustPoolForMillis | expectedWaitingSpans | expectedTimeout
63-
1000 | null | 0 | false
64-
1000 | null | 0 | false
65-
1000 | 500 | 1 | false
66-
1000 | 1500 | 1 | true
69+
createDataSource | exhaustPoolForMillis | expectedWaitingSpans | expectedTimeout
70+
this.&hikariDataSource | null | 0 | false
71+
this.&hikariDataSource | null | 0 | false
72+
this.&hikariDataSource | 500 | 1 | false
73+
this.&hikariDataSource | 1500 | 1 | true
74+
this.&dbcp2DataSource | null | 0 | false
75+
this.&dbcp2DataSource | null | 0 | false
76+
this.&dbcp2DataSource | 500 | 1 | false
77+
this.&dbcp2DataSource | 1500 | 1 | true
78+
}
79+
80+
private static DataSource hikariDataSource() {
81+
final HikariConfig config = new HikariConfig()
82+
config.setPoolName("testPool")
83+
config.setMaximumPoolSize(1)
84+
config.setConnectionTimeout(CONNECTION_TIMEOUT)
85+
config.setDataSourceClassName(TestDataSource.class.getName())
86+
return new HikariDataSource(config)
87+
}
88+
89+
private static DataSource dbcp2DataSource() {
90+
final BasicDataSource ds = new BasicDataSource()
91+
ds.setMaxTotal(1)
92+
ds.setMaxWait(Duration.ofMillis(CONNECTION_TIMEOUT))
93+
ds.setDriverClassName(TestDriver.class.getName())
94+
ds.start()
95+
return ds
6796
}
6897
}

dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ class TestConnection implements Connection {
227227

228228
@Override
229229
boolean isValid(int timeout) throws SQLException {
230-
return false
230+
return true
231231
}
232232

233233
@Override

dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestDriver.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class TestDriver implements Driver {
1515

1616
@Override
1717
boolean acceptsURL(String url) throws SQLException {
18-
return false
18+
return true
1919
}
2020

2121
@Override

0 commit comments

Comments
 (0)