Skip to content

Add SQL database semantic conventions to AWS SDK v2 RDS Data API spans #19256

Description

@rsclarke

Is your feature request related to a problem? Please describe.

The AWS SDK v2 instrumentation currently traces Amazon RDS Data API calls as generic AWS RPC client spans. For example, ExecuteStatement produces a span similar to:

name: RdsData.ExecuteStatement
kind: CLIENT

rpc.system: aws-api
rpc.service: RdsData
rpc.method: ExecuteStatement

This preserves the AWS operation identity, request ID, HTTP attributes, and error information, but does not describe the SQL operation using the database semantic conventions. In particular, the span has no:

  • db.system.name
  • db.namespace
  • db.query.text
  • db.query.summary
  • db.operation.batch.size

Applications using the RDS Data API therefore do not get the query naming, sanitization, database metrics, or analysis available when the same SQL is executed through the JDBC instrumentation.

Describe the solution you'd like

Enrich the existing AWS SDK client span for RDS Data API SQL operations using the shared SQL instrumentation APIs from opentelemetry-instrumentation-api-incubator, following the same abstraction boundary as JDBC:

  • SqlClientAttributesGetter
  • SqlClientAttributesExtractor
  • DbClientSpanNameExtractor
  • DbClientMetrics
  • the database client exception event extractor

The initial implementation should cover the currently supported SQL execution operations:

  • ExecuteStatement
  • BatchExecuteStatement

Whether to include the deprecated ExecuteSql operation in the initial implementation is an open question described under Additional context.

Transaction-control operations should remain generic AWS RPC spans in the initial change:

  • BeginTransaction
  • CommitTransaction
  • RollbackTransaction

These operations do not contain SQL text, and JDBC transaction spans are currently experimental.

Enrich the existing AWS span

The implementation should enrich the existing AWS SDK span rather than create a nested database span. One RDS Data API request represents one logical remote database operation, so a second child span would duplicate the operation and its duration. This is also consistent with the AWS SDK DynamoDB instrumentation, which adds database attributes and metrics to the existing AWS SDK span.

The existing AWS RPC, request ID, HTTP, URL, and error attributes should remain on the span.

Map request data through AWS SDK core APIs

The RDS Data API request can be inspected through SdkRequest.getValueForField without linking against generated rdsdata classes at runtime:

Database concept RDS Data API request field
Raw query text sql
Deprecated ExecuteSql query text sqlStatements
Database namespace database
Batch size number of entries in parameterSets
Parameterized ExecuteStatement non-empty parameters
Parameterized BatchExecuteStatement at least one non-empty nested parameter set

The implementation should not capture:

  • SQL parameter values
  • secret ARNs
  • resource ARNs as database addresses
  • transaction IDs
  • database users or connection strings

The schema request field should initially be ignored. It is unsupported for the modern operations and is ambiguous across Aurora MySQL and Aurora PostgreSQL.

Use a conservative database system and SQL dialect

The request does not identify whether the target cluster uses Aurora MySQL or Aurora PostgreSQL, and the resource ARN does not encode the engine. The proposed value is therefore:

db.system.name = other_sql

other_sql matches the semantic-convention definition for a target known to use SQL when the concrete DBMS product is unavailable to the instrumentation. RDS Data is a transport rather than a DBMS, so an RDS Data-specific database-system identifier should not be introduced.

The SQL dialect should use DOUBLE_QUOTES_ARE_STRING_LITERALS, as JDBC does when the database system is unknown. This avoids potentially exposing double-quoted sensitive literals, at the cost of occasionally sanitizing quoted identifiers.

Reuse JDBC-aligned query sanitization and summarization

The shared SQL extractor should retain its default sanitization behavior:

  • Non-parameterized SQL is sanitized.
  • Parameterized SQL retains its query template under stable database semantic conventions.
  • Parameter values are never captured.
  • db.query.summary is generated by the same SQL analyzer used by JDBC.

For example:

SELECT * FROM customers WHERE email = 'person@example.com'

would produce approximately:

db.query.text: SELECT * FROM customers WHERE email = ?
db.query.summary: SELECT customers

while:

SELECT * FROM customers WHERE email = :email

with a corresponding RDS Data API parameter would retain the parameterized query text without recording the value of :email.

SqlClientAttributesExtractorBuilder.setSingleOperationAndCollection(true) should not be enabled. Aurora MySQL and PostgreSQL support SQL that references multiple operations or collections in a single non-batch query, so deriving db.operation.name or db.collection.name from arbitrary SQL would conflict with the SQL semantic-convention guidance. This also matches the current JDBC instrumentation. The generated db.query.summary provides the low-cardinality operation and target description instead.

Model BatchExecuteStatement using parameter-set count

For BatchExecuteStatement, each parameter set represents one database operation. The shared database instrumentation should therefore model:

Parameter-set count Result
0 Empty batch; db.operation.batch.size = 0
1 Single operation; no batch-size attribute or BATCH prefix
2+ Batch operation; emit db.operation.batch.size and prefix the summary with BATCH

For example, two parameter sets executing:

INSERT INTO customers (id) VALUES (:id)

would produce:

db.query.summary: BATCH INSERT customers
db.operation.batch.size: 2

Preserve the existing AWS operation span name in legacy-only mode

Using DbClientSpanNameExtractor unconditionally would rename existing spans and could disrupt dashboards or alerts based on AWS operation names. The proposed compatibility behavior is:

Database semantic-convention mode Span name
Legacy-only (current 2.x default) Preserve RdsData.<AWS operation>
Stable-only (database) Use db.query.summary
Dual emit (database/dup) Use the stable query-summary name

Legacy-only mode would still gain JDBC-aligned legacy database attributes such as sanitized db.statement, db.operation, db.name, and db.system through the shared extractor.

For example, stable database semantic conventions could change an ExecuteStatement span from:

RdsData.ExecuteStatement

to:

SELECT customers

Add database metrics and error semantics

The enriched instrumenter should add:

  • db.client.operation.duration through DbClientMetrics
  • database client error semantics and exception event naming

No database-native status code appears to be available from the RDS Data API response. error.type should therefore fall back to the relevant AWS SDK exception class rather than using the HTTP status code as a database status code.

Avoid an RDS Data runtime dependency

Production code should continue depending only on AWS SDK core APIs. Request recognition can use the existing string-based AwsSdkRequest mechanism with exact suffixes:

rdsdata.model.ExecuteStatementRequest
rdsdata.model.BatchExecuteStatementRequest
rdsdata.model.ExecuteSqlRequest

The rdsdata.model. prefix is important because DynamoDB has operations with the same request class names for PartiQL. The generated RDS Data SDK module should only be a test dependency. AWS SDK v2 2.5.54 is the first release containing both ExecuteStatement and BatchExecuteStatement.

Test without live AWS infrastructure

The tests should not require a live RDS or Aurora instance, Docker, or LocalStack. They can follow the existing AWS SDK test pattern by configuring the generated RDS Data client with a local MockWebServerExtension endpoint and static test credentials.

This exercises SDK request marshalling, signing, execution interceptors, sync and async behavior, response handling, and emitted telemetry without making AWS calls. The tests would validate instrumentation behavior rather than whether Aurora accepts or executes a particular SQL statement.

Use isolated AWS SDK 2.5.54+ test suites so that adding the RDS Data dependency does not transitively raise the repository's AWS SDK 2.2.0 floor for existing tests. Run the suites in legacy-only and stable-only database-semconv modes for library instrumentation, javaagent instrumentation, and library autoconfigure.

Test coverage should include:

  • synchronous and asynchronous clients
  • ExecuteStatement and BatchExecuteStatement
  • ExecuteSql if included in the accepted scope
  • legacy-only AWS operation names and stable query-summary names
  • literal sanitization and parameterized query text
  • batch sizes 0, 1, and 2
  • database namespace extraction
  • stable database metrics
  • errors and error.type
  • retained AWS RPC, HTTP, and request ID attributes
  • absence of parameter values, secrets, transaction IDs, and ARNs
  • transaction-control operations remaining generic AWS spans

Proposed implementation outline

  1. Add an RDS Data request type and exact mappings for the accepted SQL operations.
  2. Add an AWS SDK-core-only SqlClientAttributesGetter for RDS Data requests.
  3. Add an RDS Data instrumenter using the shared SQL extractor, name extractor, metrics, and database exception conventions.
  4. Route only the selected SQL request classes to that instrumenter.
  5. Preserve transaction and other RDS Data API calls as generic AWS spans.
  6. Add isolated AWS SDK 2.5.54+ test suites for library, javaagent, and library autoconfigure.
  7. Run the tests under legacy-only and stable-only database semantic conventions at minimum and latest AWS SDK versions.

Describe alternatives you've considered

Create a nested database span

This would preserve the outer AWS RPC span unchanged, but it would represent one logical RDS Data API operation twice and duplicate its duration. There is no independent lower-level database operation observable to the client, so enriching the existing span is more accurate.

Add instrumentation to an individual RDS Data API wrapper library

This could provide SQL spans for that library's consumers, but would not help other AWS SDK users and would duplicate parsing, sanitization, and semantic-convention behavior already available in the upstream instrumentation.

Implement a separate SQL parser or regular-expression-based span naming

This would risk diverging from JDBC on comments, quoted identifiers, joins, CTEs, nested queries, multi-statement SQL, sanitization, and summary cardinality. Reusing the shared SQL analyzer keeps the behavior aligned.

Rename spans unconditionally

This would provide query-summary names in all modes, but would break existing dashboards and alerts that use RdsData.<operation>. Restricting the rename to stable database semantic conventions provides an explicit migration path.

Infer the Aurora engine from the resource ARN

Aurora cluster ARNs do not encode whether the engine is MySQL or PostgreSQL. Determining the engine would require an additional RDS API request or user-provided ARN-to-engine configuration, adding latency, permissions, caching, and configuration complexity. other_sql is a safer default.

Enrich transaction-control operations in the same change

These requests have no SQL text, and the comparable JDBC transaction instrumentation is experimental. Keeping them generic avoids expanding the initial semantic-convention scope.

Additional context

Open questions

  1. Is other_sql the appropriate db.system.name?

    The instrumentation cannot determine whether the request targets Aurora MySQL or Aurora PostgreSQL without an additional API call or user-provided mapping. other_sql appears preferable to inventing an RDS Data-specific DBMS identifier because RDS Data is a transport.

  2. Should query-summary span naming be limited to stable database semantic conventions?

    The proposal preserves RdsData.<operation> in legacy-only mode and uses the SQL summary in stable-only and dual-emission modes. Maintainer feedback on this compatibility policy would be useful.

  3. Should the deprecated ExecuteSql operation be included?

    ExecuteSql has existed since AWS SDK v2 2.1.1, but is deprecated, supported only for Aurora Serverless v1, and replaced by ExecuteStatement and BatchExecuteStatement.

    It can technically use the same instrumentation by reading sqlStatements and passing the complete semicolon-separated value to the shared SQL analyzer. The analyzer already handles multi-statement text and can produce a sanitized query and a summary such as:

    SELECT customers; UPDATE orders
    

    It should be treated as one multi-statement query, not as a batch. Possible scopes are:

    • include it in the initial change for existing Serverless v1 users;
    • limit the initial change to the two supported operations and add it separately;
    • intentionally leave it as a generic AWS RPC span.
  4. Should transaction operations remain generic in the initial change?

    The proposal leaves them unchanged until there is agreement on whether RDS Data API transaction spans should follow JDBC's experimental transaction instrumentation.

References

Disclaimer

Yes I used AI to analyse the existing JDBC instrumentation and devise this approach for enriching the RDS Data API.

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestneeds triageNew issue that requires triage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions