Merge pull request #382 from DataDog/gary/jdbc-cache-investigation

Fixes #379 proxy connection caching issue
This commit is contained in:
Gary Huang 2018-07-09 18:19:12 -04:00 committed by GitHub
commit 77717d305b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 447 additions and 205 deletions

View File

@ -1,5 +1,13 @@
apply from: "${rootDir}/gradle/java.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
latestDepTest {
dirName = 'test'
}
}
dependencies {
compile project(':dd-java-agent:agent-tooling')
@ -12,7 +20,24 @@ dependencies {
// jdbc unit testing
testCompile group: 'com.h2database', name: 'h2', version: '1.4.197'
testCompile group: 'org.apache.derby', name: 'derby', version: '10.12.1.1'
// not using hsqldb 2.4 because org/hsqldb/jdbc/JDBCDriver : Unsupported major.minor version 52.0
testCompile group: 'org.hsqldb', name: 'hsqldb', version: '2.3.+'
testCompile group: 'org.apache.tomcat', name: 'tomcat-jdbc', version: '7.0.19'
// tomcat needs this to run
testCompile group: 'org.apache.tomcat', name: 'tomcat-juli', version: '7.0.19'
testCompile group: 'com.zaxxer', name: 'HikariCP', version: '2.4.0'
testCompile group: 'com.mchange', name: 'c3p0', version: '0.9.5'
}
configurations.latestDepTestCompile {
resolutionStrategy {
force group: 'com.h2database', name: 'h2', version: '+'
force group: 'org.apache.derby', name: 'derby', version: '+'
force group: 'org.hsqldb', name: 'hsqldb', version: '+'
force group: 'org.apache.tomcat', name: 'tomcat-jdbc', version: '+'
force group: 'org.apache.tomcat', name: 'tomcat-juli', version: '+'
force group: 'com.zaxxer', name: 'HikariCP', version: '+'
force group: 'com.mchange', name: 'c3p0', version: '+'
}
}

View File

@ -56,9 +56,13 @@ public final class PreparedStatementInstrumentation extends Instrumenter.Default
return null;
}
final String sql = JDBCMaps.preparedStatements.get(statement);
final Connection connection;
Connection connection;
try {
connection = statement.getConnection();
// unwrap the connection to cache the underlying actual connection and to not cache proxy objects
if (connection.isWrapperFor(Connection.class)) {
connection = connection.unwrap(Connection.class);
}
} catch (final Throwable e) {
// Had some problem getting the connection.
return NoopScope.INSTANCE;

View File

@ -57,9 +57,13 @@ public final class StatementInstrumentation extends Instrumenter.Default {
if (callDepth > 0) {
return null;
}
final Connection connection;
Connection connection;
try {
connection = statement.getConnection();
if (connection.isWrapperFor(Connection.class)) {
// unwrap the connection to cache the underlying actual connection and to not cache proxy objects
connection = connection.unwrap(Connection.class);
}
} catch (final Throwable e) {
// Had some problem getting the connection.
return NoopScope.INSTANCE;

View File

@ -1,42 +1,145 @@
import com.mchange.v2.c3p0.ComboPooledDataSource
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.api.DDSpanTypes
import io.opentracing.tag.Tags
import org.apache.derby.jdbc.EmbeddedDriver
import org.h2.Driver
import org.hsqldb.jdbc.JDBCDriver
import spock.lang.Shared
import spock.lang.Unroll
import javax.sql.DataSource
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.Statement
import static datadog.trace.agent.test.ListWriterAssert.assertTraces
class JDBCInstrumentationTest extends AgentTestRunner {
@Shared
def dbName = "jdbcUnitTest"
@Shared
private Map<String, Connection> connections
private Map<String, String> jdbcUrls = [
h2 : "jdbc:h2:mem:" + dbName,
derby : "jdbc:derby:memory:" + dbName,
hsqldb: "jdbc:hsqldb:mem:" + dbName
]
@Shared
private Map<String, String> jdbcDriverClassNames = [
h2 : "org.h2.Driver",
derby : "org.apache.derby.jdbc.EmbeddedDriver",
hsqldb: "org.hsqldb.jdbc.JDBCDriver"
]
@Shared
private Map<String, String> jdbcUserNames = [
h2 : null,
derby : "APP",
hsqldb: "SA"
]
// JDBC Connection pool name (i.e. HikariCP) -> Map<dbName, Datasource>
@Shared
private Map<String, Map<String, DataSource>> cpDatasources = new HashMap<>()
def prepareConnectionPoolDatasources() {
String[] connectionPoolNames = [
"tomcat", "hikari", "c3p0"
]
connectionPoolNames.each {
cpName ->
Map<String, DataSource> dbDSMapping = new HashMap<>()
jdbcUrls.each {
dbType, jdbcUrl ->
dbDSMapping.put(dbType, createDS(cpName, dbType, jdbcUrl))
}
cpDatasources.put(cpName, dbDSMapping)
}
}
def createTomcatDS(String dbType, String jdbcUrl) {
DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource()
def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl
ds.setUrl(jdbcUrlToSet)
ds.setDriverClassName(jdbcDriverClassNames.get(dbType))
String username = jdbcUserNames.get(dbType)
if (username != null) {
ds.setUsername(username)
}
ds.setPassword("")
ds.setMaxActive(1) // to test proper caching, having > 1 max active connection will be hard to
// determine whether the connection is properly cached
return ds
}
def createHikariDS(String dbType, String jdbcUrl) {
HikariConfig config = new HikariConfig()
def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl
config.setJdbcUrl(jdbcUrlToSet)
String username = jdbcUserNames.get(dbType)
if (username != null) {
config.setUsername(username)
}
config.setPassword("")
config.addDataSourceProperty("cachePrepStmts", "true")
config.addDataSourceProperty("prepStmtCacheSize", "250")
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048")
config.setMaximumPoolSize(1)
return new HikariDataSource(config)
}
def createC3P0DS(String dbType, String jdbcUrl) {
DataSource ds = new ComboPooledDataSource()
ds.setDriverClass(jdbcDriverClassNames.get(dbType))
def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl
ds.setJdbcUrl(jdbcUrlToSet)
String username = jdbcUserNames.get(dbType)
if (username != null) {
ds.setUser(username)
}
ds.setPassword("")
ds.setMaxPoolSize(1)
return ds
}
def createDS(String connectionPoolName, String dbType, String jdbcUrl) {
DataSource ds = null
if (connectionPoolName == "tomcat") {
ds = createTomcatDS(dbType, jdbcUrl)
}
if (connectionPoolName == "hikari") {
ds = createHikariDS(dbType, jdbcUrl)
}
if (connectionPoolName == "c3p0") {
ds = createC3P0DS(dbType, jdbcUrl)
}
return ds
}
def setupSpec() {
Connection h2Connection = new Driver().connect("jdbc:h2:mem:" + dbName, null)
Connection hsqlConnection = new JDBCDriver().connect("jdbc:hsqldb:mem:" + dbName, null)
Connection derbyConnection = new EmbeddedDriver().connect("jdbc:derby:memory:" + dbName + ";create=true", null)
connections = [
h2 : h2Connection,
derby : derbyConnection,
hsqldb: hsqlConnection,
]
prepareConnectionPoolDatasources()
}
def cleanupSpec() {
connections.values().each {
it.close()
cpDatasources.values().each {
it.values().each {
datasource ->
if (datasource instanceof Closeable) {
datasource.close()
}
}
}
}
@Unroll
def "basic statement on #driver generates spans"() {
def "basic statement with #connection.getClass().getCanonicalName() on #driver generates spans"() {
setup:
Statement statement = connection.createStatement()
ResultSet resultSet = statement.executeQuery(query)
@ -44,45 +147,52 @@ class JDBCInstrumentationTest extends AgentTestRunner {
expect:
resultSet.next()
resultSet.getInt(1) == 3
TEST_WRITER.size() == 1
def trace = TEST_WRITER.firstTrace()
trace.size() == 1
def span = trace[0]
span.context().operationName == "${driver}.query"
span.serviceName == driver
span.resourceName == query
span.type == "sql"
!span.context().getErrorFlag()
span.context().parentId == 0
def tags = span.context().tags
tags["db.type"] == driver
tags["db.user"] == username
tags["span.kind"] == "client"
tags["component"] == "java-jdbc-statement"
tags["db.jdbc.url"].contains(driver)
tags["span.origin.type"] != null
tags["thread.name"] != null
tags["thread.id"] != null
tags.size() == username == null ? 7 : 8
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
operationName "${driver}.query"
serviceName driver
resourceName query
spanType DDSpanTypes.SQL
errored false
tags {
"db.type" driver
if (username != null) {
"db.user" username
}
"span.kind" Tags.SPAN_KIND_CLIENT
"span.type" DDSpanTypes.SQL
"component" "java-jdbc-statement"
"db.jdbc.url" jdbcUrls.get(driver)
"span.origin.type" String
defaultTags()
}
}
}
}
cleanup:
statement.close()
connection.close()
where:
driver | connection | username | query
"h2" | connections.get("h2") | null | "SELECT 3"
"derby" | connections.get("derby") | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | connections.get("hsqldb") | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
driver | connection | username | query
"h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "SELECT 3"
"derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null)| "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | new JDBCDriver().connect(jdbcUrls.get("hsqldb"), null) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
"h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | cpDatasources.get("tomcat").get("hsqldb").getConnection()| "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
"h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | cpDatasources.get("hikari").get("hsqldb").getConnection()| "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
"h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | cpDatasources.get("c3p0").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
}
@Unroll
def "prepared statement execute on #driver generates a span"() {
def "prepared statement execute on #driver with #connection.getClass().getCanonicalName() generates a span"() {
setup:
PreparedStatement statement = connection.prepareStatement(query)
assert statement.execute()
@ -91,45 +201,48 @@ class JDBCInstrumentationTest extends AgentTestRunner {
expect:
resultSet.next()
resultSet.getInt(1) == 3
TEST_WRITER.size() == 1
def trace = TEST_WRITER.firstTrace()
trace.size() == 1
def span = trace[0]
span.context().operationName == "${driver}.query"
span.serviceName == driver
span.resourceName == query
span.type == "sql"
!span.context().getErrorFlag()
span.context().parentId == 0
def tags = span.context().tags
tags["db.type"] == driver
tags["db.user"] == username
tags["span.kind"] == "client"
tags["component"] == "java-jdbc-prepared_statement"
tags["db.jdbc.url"].contains(driver)
tags["span.origin.type"] != null
tags["thread.name"] != null
tags["thread.id"] != null
tags.size() == username == null ? 7 : 8
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
operationName "${driver}.query"
serviceName driver
resourceName query
spanType DDSpanTypes.SQL
errored false
tags {
"db.type" driver
if (username != null) {
"db.user" username
}
"span.kind" Tags.SPAN_KIND_CLIENT
"span.type" DDSpanTypes.SQL
"component" "java-jdbc-prepared_statement"
"db.jdbc.url" jdbcUrls.get(driver)
"span.origin.type" String
defaultTags()
}
}
}
}
cleanup:
statement.close()
connection.close()
where:
driver | connection | username | query
"h2" | connections.get("h2") | null | "SELECT 3"
"derby" | connections.get("derby") | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | connections.get("hsqldb") | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
driver | connection | username | query
"h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "SELECT 3"
"derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null)| "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
}
@Unroll
def "prepared statement query on #driver generates a span"() {
def "prepared statement query on #driver with #connection.getClass().getCanonicalName() generates a span"() {
setup:
PreparedStatement statement = connection.prepareStatement(query)
ResultSet resultSet = statement.executeQuery()
@ -137,45 +250,48 @@ class JDBCInstrumentationTest extends AgentTestRunner {
expect:
resultSet.next()
resultSet.getInt(1) == 3
TEST_WRITER.size() == 1
def trace = TEST_WRITER.firstTrace()
trace.size() == 1
def span = trace[0]
span.context().operationName == "${driver}.query"
span.serviceName == driver
span.resourceName == query
span.type == "sql"
!span.context().getErrorFlag()
span.context().parentId == 0
def tags = span.context().tags
tags["db.type"] == driver
tags["db.user"] == username
tags["span.kind"] == "client"
tags["component"] == "java-jdbc-prepared_statement"
tags["db.jdbc.url"].contains(driver)
tags["span.origin.type"] != null
tags["thread.name"] != null
tags["thread.id"] != null
tags.size() == username == null ? 7 : 8
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
operationName "${driver}.query"
serviceName driver
resourceName query
spanType DDSpanTypes.SQL
errored false
tags {
"db.type" driver
if (username != null) {
"db.user" username
}
"span.kind" Tags.SPAN_KIND_CLIENT
"span.type" DDSpanTypes.SQL
"component" "java-jdbc-prepared_statement"
"db.jdbc.url" jdbcUrls.get(driver)
"span.origin.type" String
defaultTags()
}
}
}
}
cleanup:
statement.close()
connection.close()
where:
driver | connection | username | query
"h2" | connections.get("h2") | null | "SELECT 3"
"derby" | connections.get("derby") | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | connections.get("hsqldb") | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
driver | connection | username | query
"h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "SELECT 3"
"derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null)| "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
}
@Unroll
def "statement update on #driver generates a span"() {
def "statement update on #driver with #connection.getClass().getCanonicalName() generates a span"() {
setup:
Statement statement = connection.createStatement()
def sql = connection.nativeSQL(query)
@ -183,87 +299,97 @@ class JDBCInstrumentationTest extends AgentTestRunner {
expect:
!statement.execute(sql)
statement.updateCount == 0
TEST_WRITER.size() == 1
def trace = TEST_WRITER.firstTrace()
trace.size() == 1
def span = trace[0]
span.context().operationName == "${driver}.query"
span.serviceName == driver
span.resourceName == query
span.type == "sql"
!span.context().getErrorFlag()
span.context().parentId == 0
def tags = span.context().tags
tags["db.type"] == driver
tags["db.user"] == username
tags["span.kind"] == "client"
tags["component"] == "java-jdbc-statement"
tags["db.jdbc.url"].contains(driver)
tags["span.origin.type"] != null
tags["thread.name"] != null
tags["thread.id"] != null
tags.size() == username == null ? 7 : 8
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
operationName "${driver}.query"
serviceName driver
resourceName query
spanType DDSpanTypes.SQL
errored false
tags {
"db.type" driver
if (username != null) {
"db.user" username
}
"span.kind" Tags.SPAN_KIND_CLIENT
"span.type" DDSpanTypes.SQL
"component" "java-jdbc-statement"
"db.jdbc.url" jdbcUrls.get(driver)
"span.origin.type" String
defaultTags()
}
}
}
}
cleanup:
statement.close()
connection.close()
where:
driver | connection | username | query
"h2" | connections.get("h2") | null | "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | connections.get("derby") | "APP" | "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))"
"hsqldb" | connections.get("hsqldb") | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))"
driver | connection | username | query
"h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null)| "APP" | "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))"
"hsqldb" | new JDBCDriver().connect(jdbcUrls.get("hsqldb"), null) | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))"
"h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "CREATE TABLE S_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))"
"hsqldb" | cpDatasources.get("tomcat").get("hsqldb").getConnection()| "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))"
"h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "CREATE TABLE S_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))"
"hsqldb" | cpDatasources.get("hikari").get("hsqldb").getConnection()| "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))"
"h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "CREATE TABLE S_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))"
"hsqldb" | cpDatasources.get("c3p0").get("hsqldb").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))"
}
@Unroll
def "prepared statement update on #driver generates a span"() {
def "prepared statement update on #driver with #connection.getClass().getCanonicalName() generates a span"() {
setup:
def sql = connection.nativeSQL(query)
PreparedStatement statement = connection.prepareStatement(sql)
expect:
statement.executeUpdate() == 0
TEST_WRITER.size() == 1
def trace = TEST_WRITER.firstTrace()
trace.size() == 1
def span = trace[0]
span.context().operationName == "${driver}.query"
span.serviceName == driver
span.resourceName == query
span.type == "sql"
!span.context().getErrorFlag()
span.context().parentId == 0
def tags = span.context().tags
tags["db.type"] == driver
tags["db.user"] == username
tags["span.kind"] == "client"
tags["component"] == "java-jdbc-prepared_statement"
tags["db.jdbc.url"].contains(driver)
tags["span.origin.type"] != null
tags["thread.name"] != null
tags["thread.id"] != null
tags.size() == username == null ? 7 : 8
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
operationName "${driver}.query"
serviceName driver
resourceName query
spanType DDSpanTypes.SQL
errored false
tags {
"db.type" driver
if (username != null) {
"db.user" username
}
"span.kind" Tags.SPAN_KIND_CLIENT
"span.type" DDSpanTypes.SQL
"component" "java-jdbc-prepared_statement"
"db.jdbc.url" jdbcUrls.get(driver)
"span.origin.type" String
defaultTags()
}
}
}
}
cleanup:
statement.close()
connection.close()
where:
driver | connection | username | query
"h2" | connections.get("h2") | null | "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | connections.get("derby") | "APP" | "CREATE TABLE PS_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))"
"hsqldb" | connections.get("hsqldb") | "SA" | "CREATE TABLE PUBLIC.PS_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))"
driver | connection | username | query
"h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null)| "APP" | "CREATE TABLE PS_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))"
"h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "CREATE TABLE PS_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))"
"h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "CREATE TABLE PS_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))"
"h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "CREATE TABLE PS_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))"
"derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))"
}
@Unroll
@ -291,37 +417,34 @@ class JDBCInstrumentationTest extends AgentTestRunner {
then:
rs.next()
rs.getInt(1) == 3
TEST_WRITER.size() == 1
def trace = TEST_WRITER.firstTrace()
trace.size() == 1
and:
def span = trace[0]
span.context().operationName == "${driver}.query"
span.serviceName == driver
span.resourceName == query
span.type == "sql"
!span.context().getErrorFlag()
span.context().parentId == 0
def tags = span.context().tags
tags["db.type"] == driver
tags["db.user"] == user
tags["span.kind"] == "client"
if (prepareStatement) {
tags["component"] == "java-jdbc-prepared_statement"
} else {
tags["component"] == "java-jdbc-statement"
assertTraces(TEST_WRITER, 1) {
trace(0, 1) {
span(0) {
operationName "${driver}.query"
serviceName driver
resourceName query
spanType DDSpanTypes.SQL
errored false
tags {
"db.type" driver
if (username != null) {
"db.user" username
}
if (prepareStatement) {
"component" "java-jdbc-prepared_statement"
} else {
"component" "java-jdbc-statement"
}
"span.kind" Tags.SPAN_KIND_CLIENT
"span.type" DDSpanTypes.SQL
"db.jdbc.url" jdbcUrls.get(driver)
"span.origin.type" String
defaultTags()
}
}
}
}
tags["db.jdbc.url"].contains(driver)
tags["span.origin.type"] != null
tags["thread.name"] != null
tags["thread.id"] != null
tags.size() == user == null ? 7 : 8
cleanup:
if (statement != null) {
statement.close()
@ -331,11 +454,97 @@ class JDBCInstrumentationTest extends AgentTestRunner {
}
where:
prepareStatement | driver | driverClass | url | user | query
true | "h2" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;"
true | "derby" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
false | "h2" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;"
false | "derby" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
prepareStatement | driver | driverClass | url | username | query
true | "h2" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;"
true | "derby" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
false | "h2" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;"
false | "derby" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
}
@Unroll
def "#connectionPoolName connections should be cached in case of wrapped connections"() {
setup:
String dbType = "hsqldb"
DataSource ds = createDS(connectionPoolName, dbType, jdbcUrls.get(dbType))
String query = "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
int numQueries = 5
Connection connection = null
Statement statement = null
ResultSet rs = null
int[] res = new int[numQueries]
when:
for (int i = 0; i < numQueries; ++i) {
try {
connection = ds.getConnection()
statement = connection.prepareStatement(query)
rs = statement.executeQuery()
if (rs.next()) {
res[i] = rs.getInt(1)
} else {
res[i] = 0
}
} finally {
connection.close()
}
}
then:
for (int i = 0; i < numQueries; ++i) {
res[i] == 3
}
assertTraces(TEST_WRITER, 6) {
trace(0, 1) {
span(0) {
operationName "${dbType}.query"
serviceName dbType
resourceName "CALL USER()"
spanType DDSpanTypes.SQL
errored false
tags {
"db.type" "hsqldb"
"db.user" "SA"
"component" "java-jdbc-statement"
"span.kind" Tags.SPAN_KIND_CLIENT
"span.type" DDSpanTypes.SQL
"db.jdbc.url" jdbcUrls.get(dbType)
"span.origin.type" String
defaultTags()
}
}
}
for (int i = 1; i < numQueries + 1; ++i) {
trace(i, 1) {
span(0) {
operationName "${dbType}.query"
serviceName dbType
resourceName query
spanType DDSpanTypes.SQL
errored false
tags {
"db.type" dbType
"db.user" "SA"
"component" "java-jdbc-prepared_statement"
"span.kind" Tags.SPAN_KIND_CLIENT
"span.type" DDSpanTypes.SQL
"db.jdbc.url" jdbcUrls.get(dbType)
"span.origin.type" String
defaultTags()
}
}
}
}
}
cleanup:
if (ds instanceof Closeable) {
ds.close()
}
where:
connectionPoolName | _
"hikari" | _
"tomcat" | _
"c3p0" | _
}
}