From 10354441eac53304362d99946688fd9f2e32dc74 Mon Sep 17 00:00:00 2001 From: Joel Marcotte Date: Tue, 18 Aug 2026 17:30:09 -0400 Subject: [PATCH 1/3] Propagate Oracle service hash through ACTION --- .../instrumentation/jdbc/DBInfo.java | 9 ++ ...tractPreparedStatementInstrumentation.java | 2 + ...BMCompatibleConnectionInstrumentation.java | 3 + .../jdbc/DriverInstrumentation.java | 3 +- .../instrumentation/jdbc/JDBCDecorator.java | 33 ++++++- .../jdbc/StatementInstrumentation.java | 4 +- .../groovy/OracleInjectionForkedTest.groovy | 97 +++++++++++++++++++ .../test/groovy/test/TestConnection.groovy | 7 ++ .../datadog/trace/api/ConfigDefaults.java | 1 + .../config/TraceInstrumentationConfig.java | 2 + .../main/java/datadog/trace/api/Config.java | 14 +++ .../datadog/trace/api/ConfigTest.groovy | 21 ++++ metadata/supported-configurations.json | 8 ++ 13 files changed, 201 insertions(+), 3 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBInfo.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBInfo.java index 6b802825b47..e576245822f 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBInfo.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBInfo.java @@ -18,6 +18,7 @@ public final class DBInfo { private final String warehouse; private final String schema; private volatile String poolName; + private volatile String oracleServiceAction; DBInfo( String type, @@ -220,6 +221,14 @@ public void setPoolName(String poolname) { this.poolName = poolname; } + public synchronized boolean markOracleServiceAction(String action) { + if (action.equals(oracleServiceAction)) { + return false; + } + oracleServiceAction = action; + return true; + } + public Builder toBuilder() { return new Builder( type, diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java index f24dcb8077a..c0fb06511e6 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java @@ -81,6 +81,8 @@ public static AgentScope onEnter(@Advice.This final Statement statement) { connection, InstrumentationContext.get(Connection.class, DBInfo.class)); final boolean injectTraceContext = DECORATE.shouldInjectTraceContext(dbInfo); + DECORATE.setServiceHashAction(connection, dbInfo); + if (INJECT_COMMENT && injectTraceContext) { if (DECORATE.isSqlServer(dbInfo)) { // The span ID is pre-determined so that we can reference it when setting the context diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java index 486a92dcbf7..3329bb117e5 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java @@ -123,6 +123,9 @@ public static String onEnter( final DBInfo dbInfo = JDBCDecorator.parseDBInfo( connection, InstrumentationContext.get(Connection.class, DBInfo.class)); + if (!DECORATE.shouldInjectSqlComment(dbInfo)) { + return inputSql; + } String dbService = DECORATE.getDbService(dbInfo); if (dbService != null) { dbService = traceConfig(activeSpan).getServiceMapping().getOrDefault(dbService, dbService); diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java index c5c266ed312..b981b5259c0 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java @@ -113,7 +113,8 @@ public static void addDBInfo( // ignore } } - DBInfo dbInfo = JDBCConnectionUrlParser.extractDBInfo(connectionUrl, connectionProps); + DBInfo dbInfo = + JDBCConnectionUrlParser.extractDBInfo(connectionUrl, connectionProps).toBuilder().build(); InstrumentationContext.get(Connection.class, DBInfo.class).put(connWithContext, dbInfo); } } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java index 7914ef8d89c..add8639268c 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java @@ -54,6 +54,7 @@ public class JDBCDecorator extends DatabaseClientDecorator { SpanNaming.instance().namingSchema().database().service("jdbc"); public static final String DD_INSTRUMENTATION_PREFIX = "_DD_"; + public static final String DD_ORACLE_SERVICE_HASH_PREFIX = "_DD_DDSH:"; public static final String DBM_PROPAGATION_MODE = Config.get().getDbmPropagationMode(); private static final boolean DBM_INJECT_SQL_BASE_HASH = Config.get().isDbmInjectSqlBaseHash(); @@ -65,6 +66,9 @@ public class JDBCDecorator extends DatabaseClientDecorator { || DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_DYNAMIC_SERVICE); private static final boolean INJECT_TRACE_CONTEXT = DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_FULL); + private static final boolean INJECT_ORACLE_SERVICE_HASH_ACTION = + DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_DYNAMIC_SERVICE) + && Config.get().isDbmPropagationOracleActionEnabled(); public static final boolean DBM_TRACE_PREPARED_STATEMENTS = Config.get().isDbmTracePreparedStatements(); public static final boolean DBM_ALWAYS_APPEND_SQL_COMMENT = @@ -238,7 +242,7 @@ public static DBInfo parseDBInfoFromConnection(final Connection connection) { // getClientInfo is likely not allowed, we can still extract info from the url alone log.debug(LogCollector.EXCLUDE_TELEMETRY, "Could not get client info from DB", ex); } - dbInfo = JDBCConnectionUrlParser.extractDBInfo(url, clientInfo); + dbInfo = JDBCConnectionUrlParser.extractDBInfo(url, clientInfo).toBuilder().build(); } else { dbInfo = DBInfo.DEFAULT; } @@ -292,6 +296,33 @@ public boolean isSqlServer(final DBInfo dbInfo) { return "sqlserver".equals(dbInfo.getType()); } + public boolean shouldInjectSqlComment(final DBInfo dbInfo) { + return INJECT_COMMENT && !(INJECT_ORACLE_SERVICE_HASH_ACTION && isOracle(dbInfo)); + } + + /** Sets the dynamic service hash in {@code v$session.action} once per Oracle session and hash. */ + public void setServiceHashAction(Connection connection, DBInfo dbInfo) { + if (!INJECT_ORACLE_SERVICE_HASH_ACTION || !isOracle(dbInfo)) { + return; + } + + final String baseHash = BaseHash.getBaseHashStr(); + if (baseHash == null) { + return; + } + final String action = DD_ORACLE_SERVICE_HASH_PREFIX + baseHash; + if (!dbInfo.markOracleServiceAction(action)) { + return; + } + + try { + connection.setClientInfo("OCSID.ACTION", action); + } catch (Throwable e) { + // The attempt stays recorded so unsupported drivers do not pay this cost on every query. + logInjectionErrorOnce("service hash action", e); + } + } + /** * Executes `connection.setClientInfo("OCSID.ACTION", traceContext)` statement on the Oracle DB to * set the trace parent in `v$session.action`. This is used because it isn't possible to propagate diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java index 5b5ec71b671..997ab1500a5 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java @@ -93,6 +93,8 @@ public static AgentScope onEnter( final boolean isSqlServer = DECORATE.isSqlServer(dbInfo); final boolean isOracle = DECORATE.isOracle(dbInfo); + DECORATE.setServiceHashAction(connection, dbInfo); + if (INJECT_COMMENT && injectTraceContext) { if (isSqlServer) { // The span ID is pre-determined so that we can reference it when setting the context @@ -116,7 +118,7 @@ public static AgentScope onEnter( DECORATE.afterStart(span); DECORATE.onConnection(span, dbInfo); final String copy = sql; - if (span != null && INJECT_COMMENT) { + if (span != null && DECORATE.shouldInjectSqlComment(dbInfo)) { String traceParent = null; if (injectTraceContext) { diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy index e13fbcdc734..4b625a22a18 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy @@ -1,5 +1,9 @@ import datadog.trace.agent.test.InstrumentationSpecification +import datadog.trace.api.BaseHash +import datadog.trace.api.DDSpanTypes +import datadog.trace.api.ProcessTags import datadog.trace.api.config.TraceInstrumentationConfig +import datadog.trace.bootstrap.instrumentation.api.Tags import test.TestConnection import test.TestDatabaseMetaData import test.TestPreparedStatement @@ -75,3 +79,96 @@ class OracleInjectionForkedTest extends OracleInjectionTestBase { serviceNameUrl | serviceNameInjection } } + +class OracleDynamicServiceActionInjectionForkedTest extends OracleInjectionTestBase { + @Override + void configurePreAgent() { + super.configurePreAgent() + + injectSysConfig(TraceInstrumentationConfig.DB_DBM_PROPAGATION_MODE_MODE, "dynamic_service") + injectSysConfig( + TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED, "true") + } + + def setup() { + ProcessTags.reset() + BaseHash.updateBaseHash(-6937226773133363462L) + } + + def "Oracle dynamic service mode propagates the hash in ACTION without changing statement SQL"() { + setup: + def connection = createOracleConnection(serviceNameUrl) + def statement = connection.createStatement() as TestStatement + + when: + statement.executeQuery(query) + statement.executeQuery(query) + + then: + statement.sql == query + connection.clientInfoName == "OCSID.ACTION" + connection.clientInfoValue == "_DD_DDSH:-6937226773133363462" + connection.clientInfoSetCount == 1 + assertTraces(2) { + trace(1) { + span { + spanType DDSpanTypes.SQL + tags(false) { + "$Tags.BASE_HASH" "-6937226773133363462" + } + } + } + trace(1) { + span { + spanType DDSpanTypes.SQL + tags(false) { + "$Tags.BASE_HASH" "-6937226773133363462" + } + } + } + } + } + + def "Oracle dynamic service mode preserves prepared statement SQL"() { + setup: + def connection = createOracleConnection(sidUrl) + + when: + def statement = connection.prepareStatement(query) as TestPreparedStatement + statement.execute() + + then: + statement.sql == query + connection.clientInfoValue == "_DD_DDSH:-6937226773133363462" + connection.clientInfoSetCount == 1 + } + + def "Oracle dynamic service mode refreshes ACTION only when the hash changes"() { + setup: + def connection = createOracleConnection(serviceNameUrl) + def statement = connection.createStatement() as TestStatement + + when: + statement.executeQuery(query) + BaseHash.updateBaseHash(123456789L) + statement.executeQuery(query) + + then: + connection.clientInfoValue == "_DD_DDSH:123456789" + connection.clientInfoSetCount == 2 + } + + def "Oracle dynamic service mode initializes every connection with the same URL"() { + setup: + def firstConnection = createOracleConnection(serviceNameUrl) + def secondConnection = createOracleConnection(serviceNameUrl) + + when: + firstConnection.createStatement().executeQuery(query) + secondConnection.createStatement().executeQuery(query) + + then: + firstConnection.clientInfoSetCount == 1 + secondConnection.clientInfoSetCount == 1 + } +} diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy index c710a6c89bb..2283abad7e9 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy @@ -22,6 +22,10 @@ import java.util.concurrent.Executor * A JDBC connection class that optionally throws an exception in the constructor, used to test */ class TestConnection implements Connection { + public String clientInfoName + public String clientInfoValue + public int clientInfoSetCount + TestConnection(boolean throwException) { if (throwException) { throw new RuntimeException("connection exception") @@ -232,6 +236,9 @@ class TestConnection implements Connection { @Override void setClientInfo(String name, String value) throws SQLClientInfoException { + clientInfoName = name + clientInfoValue = value + clientInfoSetCount++ } @Override diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index 991c6fd6937..ed33e69854f 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -73,6 +73,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX = false; static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_HOST = false; static final String DEFAULT_DB_DBM_PROPAGATION_MODE_MODE = "disabled"; + static final boolean DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED = false; static final boolean DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS = false; static final boolean DEFAULT_DB_DBM_ALWAYS_APPEND_SQL_COMMENT = false; // Default value is set to 0, it disables the latency trace interceptor diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java index 855a7243caf..8aa4c550830 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java @@ -74,6 +74,8 @@ public final class TraceInstrumentationConfig { public static final String DB_DBM_INJECT_SQL_BASEHASH = "dbm.inject.sql.basehash"; public static final String DB_DBM_PROPAGATION_MODE_MODE = "dbm.propagation.mode"; + public static final String DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED = + "dbm.propagation.oracle.action.enabled"; public static final String DB_DBM_TRACE_PREPARED_STATEMENTS = "dbm.trace_prepared_statements"; public static final String DB_DBM_ALWAYS_APPEND_SQL_COMMENT = "dbm.always_append_sql_comment"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index fade2b4c417..bc12b53d358 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -59,6 +59,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_ALWAYS_APPEND_SQL_COMMENT; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_MODE_MODE; +import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS; @@ -583,6 +584,7 @@ import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_ALWAYS_APPEND_SQL_COMMENT; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_INJECT_SQL_BASEHASH; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_PROPAGATION_MODE_MODE; +import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_TRACE_PREPARED_STATEMENTS; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_METADATA_FETCHING_ON_CONNECT; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_METADATA_FETCHING_ON_QUERY; @@ -1246,6 +1248,7 @@ public static String getHostName() { private final boolean dbmInjectSqlBaseHash; private final String dbmPropagationMode; + private final boolean dbmPropagationOracleActionEnabled; private final boolean dbmTracePreparedStatements; private final boolean dbmAlwaysAppendSqlComment; private final boolean dbMetadataFetchingOnQuery; @@ -1840,6 +1843,11 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins configProvider.getString( DB_DBM_PROPAGATION_MODE_MODE, DEFAULT_DB_DBM_PROPAGATION_MODE_MODE); + dbmPropagationOracleActionEnabled = + configProvider.getBoolean( + DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED, + DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED); + dbmTracePreparedStatements = configProvider.getBoolean( DB_DBM_TRACE_PREPARED_STATEMENTS, DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS); @@ -6000,6 +6008,10 @@ public String getDbmPropagationMode() { return dbmPropagationMode; } + public boolean isDbmPropagationOracleActionEnabled() { + return dbmPropagationOracleActionEnabled; + } + // Database monitoring propagation mode constants public static final String DBM_PROPAGATION_MODE_STATIC = "service"; public static final String DBM_PROPAGATION_MODE_FULL = "full"; @@ -6579,6 +6591,8 @@ public String toString() { + dbmInjectSqlBaseHash + ", dbmPropagationMode=" + dbmPropagationMode + + ", dbmPropagationOracleActionEnabled=" + + dbmPropagationOracleActionEnabled + ", dbmTracePreparedStatements=" + dbmTracePreparedStatements + ", splitByTags=" diff --git a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy index ef95d5e902c..149f38969bf 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy @@ -100,6 +100,7 @@ import static datadog.trace.api.config.RemoteConfigConfig.REMOTE_CONFIG_URL import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_HOST import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX +import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED import static datadog.trace.api.config.TraceInstrumentationConfig.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN import static datadog.trace.api.config.TraceInstrumentationConfig.RUNTIME_CONTEXT_FIELD_INJECTION import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_ENABLED @@ -3427,6 +3428,26 @@ class ConfigTest extends DDSpecification { "false" | "true" | false // sys prop takes precedence } + def "Oracle DBM action propagation enabled = #configured"() { + setup: + def properties = new Properties() + if (configured != null) { + properties.setProperty(DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED, configured) + } + + when: + def config = new Config(ConfigProvider.withPropertiesOverride(properties)) + + then: + config.isDbmPropagationOracleActionEnabled() == expected + + where: + configured | expected + null | false + "false" | false + "true" | true + } + def "trace resource renaming activation with appsec=#appsec and explicit=#explicit"() { setup: if (appsec != null) { diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index a1459dc3453..620adee1cb4 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1185,6 +1185,14 @@ "aliases": [] } ], + "DD_DBM_PROPAGATION_ORACLE_ACTION_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": [] + } + ], "DD_DBM_TRACE_PREPARED_STATEMENTS": [ { "version": "A", From ab2505d811a53d33465ff83e59c9cef6a189dfc9 Mon Sep 17 00:00:00 2001 From: Joel Marcotte Date: Wed, 19 Aug 2026 11:50:32 -0400 Subject: [PATCH 2/3] Rename Oracle ACTION-only propagation config --- .../instrumentation/jdbc/JDBCDecorator.java | 2 +- .../groovy/OracleInjectionForkedTest.groovy | 2 +- .../datadog/trace/api/ConfigDefaults.java | 2 +- .../config/TraceInstrumentationConfig.java | 4 ++-- .../main/java/datadog/trace/api/Config.java | 20 +++++++++---------- .../datadog/trace/api/ConfigTest.groovy | 6 +++--- metadata/supported-configurations.json | 2 +- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java index add8639268c..1536db95f8c 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java @@ -68,7 +68,7 @@ public class JDBCDecorator extends DatabaseClientDecorator { DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_FULL); private static final boolean INJECT_ORACLE_SERVICE_HASH_ACTION = DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_DYNAMIC_SERVICE) - && Config.get().isDbmPropagationOracleActionEnabled(); + && Config.get().isDbmPropagationOracleActionOnlyEnabled(); public static final boolean DBM_TRACE_PREPARED_STATEMENTS = Config.get().isDbmTracePreparedStatements(); public static final boolean DBM_ALWAYS_APPEND_SQL_COMMENT = diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy index 4b625a22a18..29aca51fb37 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy @@ -87,7 +87,7 @@ class OracleDynamicServiceActionInjectionForkedTest extends OracleInjectionTestB injectSysConfig(TraceInstrumentationConfig.DB_DBM_PROPAGATION_MODE_MODE, "dynamic_service") injectSysConfig( - TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED, "true") + TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED, "true") } def setup() { diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index ed33e69854f..bafb06642ae 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -73,7 +73,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX = false; static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_HOST = false; static final String DEFAULT_DB_DBM_PROPAGATION_MODE_MODE = "disabled"; - static final boolean DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED = false; + static final boolean DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED = false; static final boolean DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS = false; static final boolean DEFAULT_DB_DBM_ALWAYS_APPEND_SQL_COMMENT = false; // Default value is set to 0, it disables the latency trace interceptor diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java index 8aa4c550830..50c1079ffd1 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java @@ -74,8 +74,8 @@ public final class TraceInstrumentationConfig { public static final String DB_DBM_INJECT_SQL_BASEHASH = "dbm.inject.sql.basehash"; public static final String DB_DBM_PROPAGATION_MODE_MODE = "dbm.propagation.mode"; - public static final String DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED = - "dbm.propagation.oracle.action.enabled"; + public static final String DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED = + "dbm.propagation.oracle.action-only.enabled"; public static final String DB_DBM_TRACE_PREPARED_STATEMENTS = "dbm.trace_prepared_statements"; public static final String DB_DBM_ALWAYS_APPEND_SQL_COMMENT = "dbm.always_append_sql_comment"; diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index bc12b53d358..54c550e0ff2 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -59,7 +59,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_ALWAYS_APPEND_SQL_COMMENT; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_MODE_MODE; -import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED; +import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERMEDIATE_SPANS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_DEBUGGER_EXCEPTION_CAPTURE_INTERVAL_SECONDS; @@ -584,7 +584,7 @@ import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_ALWAYS_APPEND_SQL_COMMENT; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_INJECT_SQL_BASEHASH; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_PROPAGATION_MODE_MODE; -import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED; +import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_TRACE_PREPARED_STATEMENTS; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_METADATA_FETCHING_ON_CONNECT; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_METADATA_FETCHING_ON_QUERY; @@ -1248,7 +1248,7 @@ public static String getHostName() { private final boolean dbmInjectSqlBaseHash; private final String dbmPropagationMode; - private final boolean dbmPropagationOracleActionEnabled; + private final boolean dbmPropagationOracleActionOnlyEnabled; private final boolean dbmTracePreparedStatements; private final boolean dbmAlwaysAppendSqlComment; private final boolean dbMetadataFetchingOnQuery; @@ -1843,10 +1843,10 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins configProvider.getString( DB_DBM_PROPAGATION_MODE_MODE, DEFAULT_DB_DBM_PROPAGATION_MODE_MODE); - dbmPropagationOracleActionEnabled = + dbmPropagationOracleActionOnlyEnabled = configProvider.getBoolean( - DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED, - DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED); + DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED, + DEFAULT_DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED); dbmTracePreparedStatements = configProvider.getBoolean( @@ -6008,8 +6008,8 @@ public String getDbmPropagationMode() { return dbmPropagationMode; } - public boolean isDbmPropagationOracleActionEnabled() { - return dbmPropagationOracleActionEnabled; + public boolean isDbmPropagationOracleActionOnlyEnabled() { + return dbmPropagationOracleActionOnlyEnabled; } // Database monitoring propagation mode constants @@ -6591,8 +6591,8 @@ public String toString() { + dbmInjectSqlBaseHash + ", dbmPropagationMode=" + dbmPropagationMode - + ", dbmPropagationOracleActionEnabled=" - + dbmPropagationOracleActionEnabled + + ", dbmPropagationOracleActionOnlyEnabled=" + + dbmPropagationOracleActionOnlyEnabled + ", dbmTracePreparedStatements=" + dbmTracePreparedStatements + ", splitByTags=" diff --git a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy index 149f38969bf..550e3c317ab 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy @@ -100,7 +100,7 @@ import static datadog.trace.api.config.RemoteConfigConfig.REMOTE_CONFIG_URL import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_HOST import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX -import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED +import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED import static datadog.trace.api.config.TraceInstrumentationConfig.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN import static datadog.trace.api.config.TraceInstrumentationConfig.RUNTIME_CONTEXT_FIELD_INJECTION import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_ENABLED @@ -3432,14 +3432,14 @@ class ConfigTest extends DDSpecification { setup: def properties = new Properties() if (configured != null) { - properties.setProperty(DB_DBM_PROPAGATION_ORACLE_ACTION_ENABLED, configured) + properties.setProperty(DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED, configured) } when: def config = new Config(ConfigProvider.withPropertiesOverride(properties)) then: - config.isDbmPropagationOracleActionEnabled() == expected + config.isDbmPropagationOracleActionOnlyEnabled() == expected where: configured | expected diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 620adee1cb4..304981aa9d1 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -1185,7 +1185,7 @@ "aliases": [] } ], - "DD_DBM_PROPAGATION_ORACLE_ACTION_ENABLED": [ + "DD_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED": [ { "version": "A", "type": "boolean", From c8283cba511f5d1ab89f1dae49a0a990354492d4 Mon Sep 17 00:00:00 2001 From: Joel Marcotte Date: Wed, 19 Aug 2026 15:57:37 -0400 Subject: [PATCH 3/3] Address Oracle ACTION propagation review feedback --- .../jdbc/JDBCConnectionContextBenchmark.java | 49 +++++++++++ .../instrumentation/jdbc/DBInfo.java | 41 ++------- .../jdbc/JDBCConnectionContext.java | 41 +++++++++ .../jdbc/JDBCConnectionContextTest.java | 54 ++++++++++++ ...tractPreparedStatementInstrumentation.java | 17 ++-- ...BMCompatibleConnectionInstrumentation.java | 9 +- .../jdbc/DriverInstrumentation.java | 9 +- .../jdbc/HikariDataSourceInstrumentation.java | 11 +-- .../jdbc/IastConnectionCallSite.java | 14 +-- .../instrumentation/jdbc/JDBCDecorator.java | 86 ++++++++++++------- .../jdbc/StatementInstrumentation.java | 17 ++-- .../groovy/OracleInjectionForkedTest.groovy | 86 +++++++++++++++++++ .../test/groovy/test/TestConnection.groovy | 11 ++- 13 files changed, 347 insertions(+), 98 deletions(-) create mode 100644 dd-java-agent/agent-bootstrap/src/jmh/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContextBenchmark.java create mode 100644 dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContext.java create mode 100644 dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContextTest.java diff --git a/dd-java-agent/agent-bootstrap/src/jmh/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContextBenchmark.java b/dd-java-agent/agent-bootstrap/src/jmh/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContextBenchmark.java new file mode 100644 index 00000000000..5bc46035d41 --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/jmh/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContextBenchmark.java @@ -0,0 +1,49 @@ +package datadog.trace.bootstrap.instrumentation.jdbc; + +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +/** + * Measures the stable Oracle service-hash path. + * + *
+ * ./gradlew :dd-java-agent:agent-bootstrap:jmh \
+ *   -Pjmh.includes=JDBCConnectionContextBenchmark -Pjmh.profilers=gc
+ * 
+ */ +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@Fork(5) +@Warmup(iterations = 2) +@Measurement(iterations = 5) +@Threads(8) +public class JDBCConnectionContextBenchmark { + + @State(Scope.Thread) + public static class ConnectionState { + private static final String BASE_HASH = "-6937226773133363462"; + + final JDBCConnectionContext context = + new JDBCConnectionContext(new DBInfo.Builder().type("oracle").build()); + + @Setup + public void setup() { + context.markOracleServiceHashSet(BASE_HASH); + } + } + + @Benchmark + public boolean stableHash(ConnectionState state) { + return state.context.shouldSetOracleServiceHash(ConnectionState.BASE_HASH); + } +} diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBInfo.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBInfo.java index e576245822f..17aa109ad1e 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBInfo.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/DBInfo.java @@ -17,8 +17,6 @@ public final class DBInfo { private final Integer port; private final String warehouse; private final String schema; - private volatile String poolName; - private volatile String oracleServiceAction; DBInfo( String type, @@ -31,8 +29,7 @@ public final class DBInfo { String host, Integer port, String warehouse, - String schema, - String poolName) { + String schema) { this.type = type; this.subtype = subtype; this.fullPropagationSupport = fullPropagationSupport; @@ -44,7 +41,6 @@ public final class DBInfo { this.port = port; this.warehouse = warehouse; this.schema = schema; - this.poolName = poolName; } public static final class Builder { @@ -61,7 +57,6 @@ public static final class Builder { private String schema; private String host; private Integer port; - private String poolName; Builder() {} @@ -76,8 +71,7 @@ public static final class Builder { String host, Integer port, String warehouse, - String schema, - String poolName) { + String schema) { this.type = type; this.subtype = subtype; this.fullPropagationSupport = fullPropagationSupport; @@ -89,7 +83,6 @@ public static final class Builder { this.port = port; this.warehouse = warehouse; this.schema = schema; - this.poolName = poolName; } public Builder type(String type) { @@ -147,11 +140,6 @@ public Builder port(Integer port) { return this; } - public Builder poolName(String poolName) { - this.poolName = poolName; - return this; - } - public DBInfo build() { return new DBInfo( type, @@ -164,8 +152,7 @@ public DBInfo build() { host, port, warehouse, - schema, - poolName); + schema); } } @@ -213,22 +200,6 @@ public String getSchema() { return schema; } - public String getPoolName() { - return poolName; - } - - public void setPoolName(String poolname) { - this.poolName = poolname; - } - - public synchronized boolean markOracleServiceAction(String action) { - if (action.equals(oracleServiceAction)) { - return false; - } - oracleServiceAction = action; - return true; - } - public Builder toBuilder() { return new Builder( type, @@ -241,8 +212,7 @@ public Builder toBuilder() { host, port, warehouse, - schema, - poolName); + schema); } @Override @@ -260,8 +230,7 @@ public boolean equals(Object o) { && Objects.equals(host, dbInfo.host) && Objects.equals(port, dbInfo.port) && Objects.equals(warehouse, dbInfo.warehouse) - && Objects.equals(schema, dbInfo.schema) - && Objects.equals(poolName, dbInfo.poolName); + && Objects.equals(schema, dbInfo.schema); } @Override diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContext.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContext.java new file mode 100644 index 00000000000..dd0b2abb3f6 --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContext.java @@ -0,0 +1,41 @@ +package datadog.trace.bootstrap.instrumentation.jdbc; + +/** Immutable database metadata and mutable state scoped to one JDBC connection. */ +public final class JDBCConnectionContext { + public static final JDBCConnectionContext DEFAULT = new JDBCConnectionContext(DBInfo.DEFAULT); + + private final DBInfo dbInfo; + private volatile String poolName; + private volatile String oracleServiceHash; + private volatile boolean oracleServiceActionUnsupported; + + public JDBCConnectionContext(DBInfo dbInfo) { + this.dbInfo = dbInfo; + } + + public DBInfo getDbInfo() { + return dbInfo; + } + + public String getPoolName() { + return poolName; + } + + public void setPoolName(String poolName) { + this.poolName = poolName; + } + + /** Returns whether this connection needs the given Oracle service hash. */ + public boolean shouldSetOracleServiceHash(String baseHash) { + return !oracleServiceActionUnsupported && !baseHash.equals(oracleServiceHash); + } + + /** Records a successfully applied Oracle service hash. */ + public void markOracleServiceHashSet(String baseHash) { + oracleServiceHash = baseHash; + } + + public void markOracleServiceActionUnsupported() { + oracleServiceActionUnsupported = true; + } +} diff --git a/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContextTest.java b/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContextTest.java new file mode 100644 index 00000000000..4e7330d4ccb --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/jdbc/JDBCConnectionContextTest.java @@ -0,0 +1,54 @@ +package datadog.trace.bootstrap.instrumentation.jdbc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class JDBCConnectionContextTest { + + @Test + void keepsMutableStateSeparateFromDatabaseInfo() { + DBInfo dbInfo = new DBInfo.Builder().type("oracle").build(); + JDBCConnectionContext first = new JDBCConnectionContext(dbInfo); + JDBCConnectionContext second = new JDBCConnectionContext(dbInfo); + + assertSame(dbInfo, first.getDbInfo()); + assertSame(dbInfo, second.getDbInfo()); + assertNull(first.getPoolName()); + assertNull(second.getPoolName()); + + first.setPoolName("first-pool"); + + assertEquals("first-pool", first.getPoolName()); + assertNull(second.getPoolName()); + } + + @Test + void tracksTheLastSuccessfullySetOracleServiceHash() { + JDBCConnectionContext context = oracleContext(); + + assertTrue(context.shouldSetOracleServiceHash("123")); + + context.markOracleServiceHashSet("123"); + + assertFalse(context.shouldSetOracleServiceHash("123")); + assertTrue(context.shouldSetOracleServiceHash("456")); + } + + @Test + void skipsActionAfterDriverIsMarkedUnsupported() { + JDBCConnectionContext context = oracleContext(); + + context.markOracleServiceActionUnsupported(); + + assertFalse(context.shouldSetOracleServiceHash("123")); + } + + private static JDBCConnectionContext oracleContext() { + return new JDBCConnectionContext(new DBInfo.Builder().type("oracle").build()); + } +} diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java index c0fb06511e6..e2cfd407bfe 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java @@ -22,6 +22,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.jdbc.DBInfo; import datadog.trace.bootstrap.instrumentation.jdbc.DBQueryInfo; +import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionContext; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; @@ -48,7 +49,7 @@ public String[] helperClassNames() { public Map contextStore() { Map contextStore = new HashMap<>(4); contextStore.put("java.sql.Statement", DBQueryInfo.class.getName()); - contextStore.put("java.sql.Connection", DBInfo.class.getName()); + contextStore.put("java.sql.Connection", JDBCConnectionContext.class.getName()); return contextStore; } @@ -76,17 +77,19 @@ public static AgentScope onEnter(@Advice.This final Statement statement) { return null; } final AgentSpan span; - final DBInfo dbInfo = - JDBCDecorator.parseDBInfo( - connection, InstrumentationContext.get(Connection.class, DBInfo.class)); + final JDBCConnectionContext connectionContext = + JDBCDecorator.parseConnectionContext( + connection, + InstrumentationContext.get(Connection.class, JDBCConnectionContext.class)); + final DBInfo dbInfo = connectionContext.getDbInfo(); final boolean injectTraceContext = DECORATE.shouldInjectTraceContext(dbInfo); - DECORATE.setServiceHashAction(connection, dbInfo); + DECORATE.setServiceHashAction(connection, connectionContext); if (INJECT_COMMENT && injectTraceContext) { if (DECORATE.isSqlServer(dbInfo)) { // The span ID is pre-determined so that we can reference it when setting the context - final long spanID = DECORATE.setContextInfo(connection, dbInfo); + final long spanID = DECORATE.setContextInfo(connection, connectionContext); // we then force that pre-determined span ID for the span covering the actual query span = AgentTracer.get() @@ -107,7 +110,7 @@ public static AgentScope onEnter(@Advice.This final Statement statement) { span = startSpan("java-jdbc-prepared_statement", DATABASE_QUERY); } DECORATE.afterStart(span); - DECORATE.onConnection(span, dbInfo); + DECORATE.onConnection(span, connectionContext); DECORATE.onPreparedStatement(span, queryInfo); DECORATE.withBaseHash(span); diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java index 3329bb117e5..1565a1af0f5 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBMCompatibleConnectionInstrumentation.java @@ -20,6 +20,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.jdbc.DBInfo; import datadog.trace.bootstrap.instrumentation.jdbc.DBQueryInfo; +import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionContext; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; @@ -102,7 +103,7 @@ public void methodAdvice(MethodTransformer transformer) { public Map contextStore() { Map contextStore = new HashMap<>(4); contextStore.put("java.sql.Statement", DBQueryInfo.class.getName()); - contextStore.put("java.sql.Connection", DBInfo.class.getName()); + contextStore.put("java.sql.Connection", JDBCConnectionContext.class.getName()); return contextStore; } @@ -121,8 +122,10 @@ public static String onEnter( final String inputSql = sql; final AgentSpan activeSpan = activeSpan(); final DBInfo dbInfo = - JDBCDecorator.parseDBInfo( - connection, InstrumentationContext.get(Connection.class, DBInfo.class)); + JDBCDecorator.parseConnectionContext( + connection, + InstrumentationContext.get(Connection.class, JDBCConnectionContext.class)) + .getDbInfo(); if (!DECORATE.shouldInjectSqlComment(dbInfo)) { return inputSql; } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java index b981b5259c0..f593a8dc130 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java @@ -12,6 +12,7 @@ import datadog.trace.agent.tooling.InstrumenterModule; import datadog.trace.bootstrap.InstrumentationContext; import datadog.trace.bootstrap.instrumentation.jdbc.DBInfo; +import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionContext; import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionUrlParser; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -43,7 +44,7 @@ public ElementMatcher hierarchyMatcher() { @Override public Map contextStore() { - return singletonMap("java.sql.Connection", DBInfo.class.getName()); + return singletonMap("java.sql.Connection", JDBCConnectionContext.class.getName()); } @Override @@ -113,9 +114,9 @@ public static void addDBInfo( // ignore } } - DBInfo dbInfo = - JDBCConnectionUrlParser.extractDBInfo(connectionUrl, connectionProps).toBuilder().build(); - InstrumentationContext.get(Connection.class, DBInfo.class).put(connWithContext, dbInfo); + DBInfo dbInfo = JDBCConnectionUrlParser.extractDBInfo(connectionUrl, connectionProps); + InstrumentationContext.get(Connection.class, JDBCConnectionContext.class) + .put(connWithContext, new JDBCConnectionContext(dbInfo)); } } } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/HikariDataSourceInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/HikariDataSourceInstrumentation.java index 05517b28c1e..ecdf80c5a88 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/HikariDataSourceInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/HikariDataSourceInstrumentation.java @@ -8,7 +8,7 @@ import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; import datadog.trace.bootstrap.InstrumentationContext; -import datadog.trace.bootstrap.instrumentation.jdbc.DBInfo; +import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionContext; import java.sql.Connection; import java.util.Map; import net.bytebuddy.asm.Advice; @@ -32,7 +32,7 @@ public String instrumentedType() { @Override public Map contextStore() { - return singletonMap("java.sql.Connection", DBInfo.class.getName()); + return singletonMap("java.sql.Connection", JDBCConnectionContext.class.getName()); } @Override @@ -66,11 +66,12 @@ public static void start( if (unwrapped == null) { return; } - DBInfo dbInfo = InstrumentationContext.get(Connection.class, DBInfo.class).get(unwrapped); - if (dbInfo == null) { + JDBCConnectionContext connectionContext = + InstrumentationContext.get(Connection.class, JDBCConnectionContext.class).get(unwrapped); + if (connectionContext == null) { return; } - dbInfo.setPoolName(hikariPoolname); + connectionContext.setPoolName(hikariPoolname); } } } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/IastConnectionCallSite.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/IastConnectionCallSite.java index 79ccdfcf20f..40a1c839bde 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/IastConnectionCallSite.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/IastConnectionCallSite.java @@ -11,6 +11,7 @@ import datadog.trace.api.iast.sink.SqlInjectionModule; import datadog.trace.bootstrap.ContextStore; import datadog.trace.bootstrap.instrumentation.jdbc.DBInfo; +import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionContext; import java.sql.Connection; import javax.annotation.Nonnull; @@ -20,20 +21,21 @@ helpers = {JDBCDecorator.class}) public class IastConnectionCallSite { - private static ContextStore DB_INFO_STORE = null; + private static ContextStore CONNECTION_CONTEXT_STORE = null; @SuppressWarnings("unchecked") @Nonnull public static DBInfo getDBInfo(final Connection connection) { - if (DB_INFO_STORE == null) { - final int storeId = getContextStoreId(Connection.class.getName(), DBInfo.class.getName()); + if (CONNECTION_CONTEXT_STORE == null) { + final int storeId = + getContextStoreId(Connection.class.getName(), JDBCConnectionContext.class.getName()); final ContextStore store = getContextStore(storeId); - DB_INFO_STORE = (ContextStore) store; + CONNECTION_CONTEXT_STORE = (ContextStore) store; } - if (DB_INFO_STORE == null) { + if (CONNECTION_CONTEXT_STORE == null) { return JDBCDecorator.parseDBInfoFromConnection(connection); } else { - return JDBCDecorator.parseDBInfo(connection, DB_INFO_STORE); + return JDBCDecorator.parseConnectionContext(connection, CONNECTION_CONTEXT_STORE).getDbInfo(); } } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java index 1536db95f8c..cf880d4c21b 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java @@ -24,12 +24,15 @@ import datadog.trace.bootstrap.instrumentation.decorator.DatabaseClientDecorator; import datadog.trace.bootstrap.instrumentation.jdbc.DBInfo; import datadog.trace.bootstrap.instrumentation.jdbc.DBQueryInfo; +import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionContext; import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionUrlParser; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.sql.ClientInfoStatus; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.sql.Statement; import java.util.HashSet; @@ -55,6 +58,7 @@ public class JDBCDecorator extends DatabaseClientDecorator { public static final String DD_INSTRUMENTATION_PREFIX = "_DD_"; public static final String DD_ORACLE_SERVICE_HASH_PREFIX = "_DD_DDSH:"; + private static final String ORACLE_ACTION_CLIENT_INFO = "OCSID.ACTION"; public static final String DBM_PROPAGATION_MODE = Config.get().getDbmPropagationMode(); private static final boolean DBM_INJECT_SQL_BASE_HASH = Config.get().isDbmInjectSqlBaseHash(); @@ -68,6 +72,7 @@ public class JDBCDecorator extends DatabaseClientDecorator { DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_FULL); private static final boolean INJECT_ORACLE_SERVICE_HASH_ACTION = DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_DYNAMIC_SERVICE) + && PROPAGATE_PROCESS_TAGS && Config.get().isDbmPropagationOracleActionOnlyEnabled(); public static final boolean DBM_TRACE_PREPARED_STATEMENTS = Config.get().isDbmTracePreparedStatements(); @@ -157,23 +162,24 @@ private void setTagIfPresent(final AgentSpan span, final String key, final Strin } } - public void onConnection(final AgentSpan span, DBInfo dbInfo) { + public void onConnection(final AgentSpan span, final JDBCConnectionContext connectionContext) { + final DBInfo dbInfo = connectionContext.getDbInfo(); if (dbInfo != null) { processDatabaseType(span, dbInfo.getType()); setTagIfPresent(span, DB_WAREHOUSE, dbInfo.getWarehouse()); setTagIfPresent(span, DB_SCHEMA, dbInfo.getSchema()); - setTagIfPresent(span, DB_POOL_NAME, dbInfo.getPoolName()); + setTagIfPresent(span, DB_POOL_NAME, connectionContext.getPoolName()); } super.onConnection(span, dbInfo); } - public static DBInfo parseDBInfo( - final Connection connection, ContextStore contextStore) { + public static JDBCConnectionContext parseConnectionContext( + final Connection connection, ContextStore contextStore) { if (connection == null) { - return DBInfo.DEFAULT; + return JDBCConnectionContext.DEFAULT; } - DBInfo dbInfo = contextStore.get(connection); + JDBCConnectionContext connectionContext = contextStore.get(connection); /* * Logic to get the DBInfo from a JDBC Connection, if the connection was not created via * Driver.connect, or it has never seen before, the connectionInfo map will return null and will @@ -182,32 +188,32 @@ public static DBInfo parseDBInfo( * avoid retry overhead. */ { - if (dbInfo == null) { - // first look for injected DBInfo in wrapped delegates + if (connectionContext == null) { + // first look for injected connection context in wrapped delegates Connection conn = connection; Set connections = new HashSet<>(); connections.add(conn); try { - while (dbInfo == null) { + while (connectionContext == null) { Connection delegate = conn.unwrap(Connection.class); if (delegate == null || !connections.add(delegate)) { // cycle detected, stop looking break; } - dbInfo = contextStore.get(delegate); + connectionContext = contextStore.get(delegate); conn = delegate; } } catch (Throwable ignore) { } - if (dbInfo == null) { + if (connectionContext == null) { // couldn't find DBInfo from a previous call anywhere, so we try to fetch it from the DB - dbInfo = parseDBInfoFromConnection(connection); + connectionContext = new JDBCConnectionContext(parseDBInfoFromConnection(connection)); } - // store the DBInfo on the outermost connection instance to avoid future searches - contextStore.put(connection, dbInfo); + // store the context on the outermost connection instance to avoid future searches + contextStore.put(connection, connectionContext); } } - return dbInfo; + return connectionContext; } public String getDbService(final DBInfo dbInfo) { @@ -242,7 +248,7 @@ public static DBInfo parseDBInfoFromConnection(final Connection connection) { // getClientInfo is likely not allowed, we can still extract info from the url alone log.debug(LogCollector.EXCLUDE_TELEMETRY, "Could not get client info from DB", ex); } - dbInfo = JDBCConnectionUrlParser.extractDBInfo(url, clientInfo).toBuilder().build(); + dbInfo = JDBCConnectionUrlParser.extractDBInfo(url, clientInfo); } else { dbInfo = DBInfo.DEFAULT; } @@ -301,26 +307,48 @@ public boolean shouldInjectSqlComment(final DBInfo dbInfo) { } /** Sets the dynamic service hash in {@code v$session.action} once per Oracle session and hash. */ - public void setServiceHashAction(Connection connection, DBInfo dbInfo) { - if (!INJECT_ORACLE_SERVICE_HASH_ACTION || !isOracle(dbInfo)) { + public void setServiceHashAction(Connection connection, JDBCConnectionContext connectionContext) { + if (!INJECT_ORACLE_SERVICE_HASH_ACTION || !isOracle(connectionContext.getDbInfo())) { return; } - final String baseHash = BaseHash.getBaseHashStr(); + String baseHash = BaseHash.getBaseHashStr(); if (baseHash == null) { return; } - final String action = DD_ORACLE_SERVICE_HASH_PREFIX + baseHash; - if (!dbInfo.markOracleServiceAction(action)) { + if (!connectionContext.shouldSetOracleServiceHash(baseHash)) { return; } + synchronized (connectionContext) { + // Re-read after taking the lock so a waiting query does not restore a stale process hash. + baseHash = BaseHash.getBaseHashStr(); + if (baseHash == null || !connectionContext.shouldSetOracleServiceHash(baseHash)) { + return; + } + try { + connection.setClientInfo( + ORACLE_ACTION_CLIENT_INFO, DD_ORACLE_SERVICE_HASH_PREFIX + baseHash); + connectionContext.markOracleServiceHashSet(baseHash); + } catch (Throwable e) { + if (isUnsupportedOracleAction(e)) { + connectionContext.markOracleServiceActionUnsupported(); + } + logInjectionErrorOnce("service hash action", e); + } + } + } - try { - connection.setClientInfo("OCSID.ACTION", action); - } catch (Throwable e) { - // The attempt stays recorded so unsupported drivers do not pay this cost on every query. - logInjectionErrorOnce("service hash action", e); + private static boolean isUnsupportedOracleAction(Throwable error) { + if (error instanceof UnsupportedOperationException || error instanceof AbstractMethodError) { + return true; + } + if (error instanceof SQLClientInfoException) { + final SQLClientInfoException clientInfoException = (SQLClientInfoException) error; + return clientInfoException.getFailedProperties() != null + && ClientInfoStatus.REASON_UNKNOWN_PROPERTY.equals( + clientInfoException.getFailedProperties().get(ORACLE_ACTION_CLIENT_INFO)); } + return false; } /** @@ -360,10 +388,10 @@ public void setAction(AgentSpan span, Connection connection) { * Downsides: takes time. * * @param connection The same connection as the one that will be used for the actual statement - * @param dbInfo dbInfo of the instrumented database + * @param connectionContext metadata and state for the instrumented connection * @return spanID pre-created spanID */ - public long setContextInfo(Connection connection, DBInfo dbInfo) { + public long setContextInfo(Connection connection, JDBCConnectionContext connectionContext) { final byte VERSION = 0; final long spanID = Config.get().getIdGenerationStrategy().generateSpanId(); // potentially get build span like here @@ -373,7 +401,7 @@ public long setContextInfo(Connection connection, DBInfo dbInfo) { .withTag("dd.instrumentation", true) .start(); DECORATE.afterStart(instrumentationSpan); - DECORATE.onConnection(instrumentationSpan, dbInfo); + DECORATE.onConnection(instrumentationSpan, connectionContext); try (ContextScope scope = activateSpan(instrumentationSpan)) { final byte samplingDecision = (byte) (instrumentationSpan.forceSamplingDecision() > 0 ? 1 : 0); diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java index 997ab1500a5..9f01d5a0125 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java @@ -25,6 +25,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.bootstrap.instrumentation.jdbc.DBInfo; +import datadog.trace.bootstrap.instrumentation.jdbc.JDBCConnectionContext; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; @@ -55,7 +56,7 @@ public ElementMatcher hierarchyMatcher() { @Override public Map contextStore() { - return singletonMap("java.sql.Connection", DBInfo.class.getName()); + return singletonMap("java.sql.Connection", JDBCConnectionContext.class.getName()); } @Override @@ -85,20 +86,22 @@ public static AgentScope onEnter( } try { final Connection connection = statement.getConnection(); - final DBInfo dbInfo = - JDBCDecorator.parseDBInfo( - connection, InstrumentationContext.get(Connection.class, DBInfo.class)); + final JDBCConnectionContext connectionContext = + JDBCDecorator.parseConnectionContext( + connection, + InstrumentationContext.get(Connection.class, JDBCConnectionContext.class)); + final DBInfo dbInfo = connectionContext.getDbInfo(); boolean injectTraceContext = DECORATE.shouldInjectTraceContext(dbInfo); final AgentSpan span; final boolean isSqlServer = DECORATE.isSqlServer(dbInfo); final boolean isOracle = DECORATE.isOracle(dbInfo); - DECORATE.setServiceHashAction(connection, dbInfo); + DECORATE.setServiceHashAction(connection, connectionContext); if (INJECT_COMMENT && injectTraceContext) { if (isSqlServer) { // The span ID is pre-determined so that we can reference it when setting the context - final long spanID = DECORATE.setContextInfo(connection, dbInfo); + final long spanID = DECORATE.setContextInfo(connection, connectionContext); // we then force that pre-determined span ID for the span covering the actual query span = AgentTracer.get() @@ -116,7 +119,7 @@ public static AgentScope onEnter( } DECORATE.afterStart(span); - DECORATE.onConnection(span, dbInfo); + DECORATE.onConnection(span, connectionContext); final String copy = sql; if (span != null && DECORATE.shouldInjectSqlComment(dbInfo)) { String traceParent = null; diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy index 29aca51fb37..50525212742 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/OracleInjectionForkedTest.groovy @@ -1,3 +1,5 @@ +import static datadog.trace.api.config.GeneralConfig.EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED + import datadog.trace.agent.test.InstrumentationSpecification import datadog.trace.api.BaseHash import datadog.trace.api.DDSpanTypes @@ -171,4 +173,88 @@ class OracleDynamicServiceActionInjectionForkedTest extends OracleInjectionTestB firstConnection.clientInfoSetCount == 1 secondConnection.clientInfoSetCount == 1 } + + def "Oracle dynamic service mode retries the same hash after a transient failure"() { + setup: + def connection = createOracleConnection(serviceNameUrl) + connection.clientInfoFailuresRemaining = 1 + def statement = connection.createStatement() as TestStatement + + when: + statement.executeQuery(query) + statement.executeQuery(query) + + then: + connection.clientInfoValue == "_DD_DDSH:-6937226773133363462" + connection.clientInfoSetCount == 2 + } + + def "Oracle dynamic service mode remembers unsupported client info"() { + setup: + def connection = createOracleConnection(serviceNameUrl) + connection.clientInfoUnsupported = true + def statement = connection.createStatement() as TestStatement + + when: + statement.executeQuery(query) + statement.executeQuery(query) + + then: + connection.clientInfoValue == null + connection.clientInfoSetCount == 1 + } +} + +class OracleDynamicServiceActionProcessTagsDisabledForkedTest extends OracleInjectionTestBase { + @Override + void configurePreAgent() { + super.configurePreAgent() + + injectSysConfig(TraceInstrumentationConfig.DB_DBM_PROPAGATION_MODE_MODE, "dynamic_service") + injectSysConfig( + TraceInstrumentationConfig.DB_DBM_PROPAGATION_ORACLE_ACTION_ONLY_ENABLED, "true") + injectSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, "false") + } + + def "Oracle statement keeps SQL comments and skips ACTION when process tags are disabled"() { + setup: + def connection = createOracleConnection(serviceNameUrl) + def statement = connection.createStatement() as TestStatement + + when: + statement.executeQuery(query) + + then: + statement.sql == "/*${serviceNameInjection}*/ ${query}" + connection.clientInfoSetCount == 0 + assertTraces(1) { + trace(1) { + span { + spanType DDSpanTypes.SQL + assert !span.tags.containsKey(Tags.BASE_HASH) + } + } + } + } + + def "Oracle prepared statement keeps SQL comments and skips ACTION when process tags are disabled"() { + setup: + def connection = createOracleConnection(sidUrl) + + when: + def statement = connection.prepareStatement(query) as TestPreparedStatement + statement.execute() + + then: + statement.sql == "/*${sidInjection}*/ ${query}" + connection.clientInfoSetCount == 0 + assertTraces(1) { + trace(1) { + span { + spanType DDSpanTypes.SQL + assert !span.tags.containsKey(Tags.BASE_HASH) + } + } + } + } } diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy index 2283abad7e9..f48d5160137 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/test/TestConnection.groovy @@ -25,6 +25,8 @@ class TestConnection implements Connection { public String clientInfoName public String clientInfoValue public int clientInfoSetCount + public int clientInfoFailuresRemaining + public boolean clientInfoUnsupported TestConnection(boolean throwException) { if (throwException) { @@ -236,9 +238,16 @@ class TestConnection implements Connection { @Override void setClientInfo(String name, String value) throws SQLClientInfoException { + clientInfoSetCount++ + if (clientInfoUnsupported) { + throw new UnsupportedOperationException("client info is unsupported") + } + if (clientInfoFailuresRemaining > 0) { + clientInfoFailuresRemaining-- + throw new SQLClientInfoException() + } clientInfoName = name clientInfoValue = value - clientInfoSetCount++ } @Override