Skip to content

Client config reporting (3.x) — stage 2: full DRIVER_CONFIG report - #974

Draft
nikagra wants to merge 2 commits into
scylladb:scylla-3.xfrom
nikagra:feature/driver-config-reporting-3x-phase2
Draft

Client config reporting (3.x) — stage 2: full DRIVER_CONFIG report#974
nikagra wants to merge 2 commits into
scylladb:scylla-3.xfrom
nikagra:feature/driver-config-reporting-3x-phase2

Conversation

@nikagra

@nikagra nikagra commented Jul 27, 2026

Copy link
Copy Markdown

What

Stage 2 of driver configuration reporting for the 3.x driver: fills in the full DRIVER_CONFIG JSON report, replacing the stage-1 {"version":1} placeholder. When driver config reporting is enabled, the control connection now reports the driver's effective configuration in the approved v2 cross-driver schema shape.

Stacked on the stage-1 PR (#973). This branch is rebased directly on #973's current tip, so its diff includes the stage-1 commit until that PR merges — review the second commit (feat: populate the full DRIVER_CONFIG report (stage 2)) for stage-2 changes, and merge after stage 1.

The report (v2 schema)

All groups are populated from Configuration + Policies on each control-connection init, so the report reflects the current configuration. v2 conventions: kebab-case keys, nested objects, and omission (never null) of any key/group with no value.

  • connection / socket / control-plane — connect + read timeouts, TCP/socket options, schema-agreement and system-query (client-side) timeouts.
  • reconnection-policy / retry-policy / speculative-execution-policy — a discriminated object chosen by the configured policy class, with a custom fallback carrying the class name.
  • load-balancing-policy — normalized flags (token-aware, dc-failover, latency-awareness, shuffle), obtained by unwrapping the policy chain.
  • node-location-preference — the LB policy's DC/rack preference (dc / rack / dc-auto / rack-auto).
  • connection-pool / query-defaults / tls — pool sizing + shard-aware port, per-request defaults, and TLS booleans only (never credentials, keystores, or host lists).

To feed node-location-preference and dc-failover, this adds public getters to DCAwareRoundRobinPolicy and RackAwareRoundRobinPolicy (getLocalDc / getLocalRack / isLocalDcExplicit / isLocalRackExplicit / getUsedHostsPerRemoteDc), and makes PagingOptimizingLoadBalancingPolicy implement ChainableLoadBalancingPolicy (exposing getChildPolicy) — at runtime Cluster.Manager wraps every session's LB policy in it, so without unwrapping the report would degrade to {type:"custom"} and lose the flags and location preference.

Reporting remains fail-safe: any failure while building the report is swallowed; SESSION_ID is still emitted and only the config blob is dropped.

Reconciliation decisions (3.x ↔ schema)

  • Pool sizes fall back to the protocol-v3+ defaults (which ScyllaDB always negotiates) when PoolingOptions is still UNSET — the report is built on the control connection, before pool sizing is finalized.
  • connection-pool.type"host" (3.x has a single per-host pool class; shard distribution is handled inside it).
  • load-balancing-policy.shufflefalse (3.x has no replica-shuffle getter).
  • Custom policies → {type:"custom", name:<class simple name>} (no reflective attribute dump in v1).

⚠️ Limitations & omissions

Several settings have no public getter (or no equivalent) in the 3.x driver, so per v2's "omit what doesn't apply" they are left out of the report rather than guessed:

Field Handling Reason
connection.write (write timeout) omitted no socket write-timeout in 3.x
connection.heartbeat omitted reserved-empty in v2; the heartbeat interval has no home this schema version
control-plane.…​.server-side-ms omitted 3.x has no client-configurable server-side (USING TIMEOUT) timeout
reconnection-policy.max-attempts omitted built-in 3.x reconnection policies are unbounded / no getter
speculative-execution-policy params reported as {type:"custom"} Constant/Percentile params are not introspectable (no getters); omitted entirely when no speculative execution is configured
load-balancing-policy.shuffle best-effort false token-aware replica ordering has no getter
tls.hostname-verification best-effort false (true only for SniSSLOptions) not introspectable from a user SSLContext; the schema requires a boolean so it is not omitted
node-location-preference omitted when the LB policy carries no DC/rack notion (e.g. plain round-robin)

Everything else in the schema is populated from public getters.

Testing

Unit — DefaultDriverConfigReporterTest (27 tests, green on JDK 8/11)

  • Gating / SESSION_ID / fail-safe (6): the stage-1 cases (reporting disabled, session-id-only on pool connections, fail-safe on a build error, session-id sharing/uniqueness).
  • Report content (10): default report shape (golden), each policy discrimination branch, PagingOptimizing unwrapping, explicit dc/rack node-location-preference, server-side timestamps, TLS enabled, socket overrides.
  • Schema conformance (11, new — mirrors the 4.x sibling PR Driver config reporting — stage 2: full DRIVER_CONFIG report #968): the normative v1 JSON Schema is shipped verbatim as a test resource (driver-core/src/test/resources/config/driver-config-report-v1.schema.json) and validated via com.networknt:json-schema-validator (pinned to 1.5.x, the last minor line still targeting Java 8). One test per discriminated-union branch/optional-group case the 3.x reporter can emit, plus a negative test proving additionalProperties: false is actually enforced (an unknown top-level key is rejected).

Plus NoopDriverConfigReporterTest (2 tests, unchanged from stage 1).

Integration — DriverConfigReportingCcmTest (3 tests) against live ScyllaDB 2026.1.0 (via CCM)

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

Covers, all live-verified: SESSION_ID shared across every connection of a session and across multiple Sessions from the same Cluster; DRIVER_CONFIG stored on exactly the control connection as the full v2 report (spot-checked for connection, load-balancing-policy, connection-pool.shard-aware.enabled — no truncation); nothing stored when reporting is disabled.

The stored DRIVER_CONFIG for the control connection, formatted:

{
  "version": 1,
  "connection": { "connect": { "timeout-ms": 5000 }, "read": { "timeout-ms": 12000 } },
  "socket": { "tcp-no-delay": true, "keep-alive": false, "reuse-address": false },
  "control-plane": {
    "system-queries": { "timeout": { "client-side-ms": 12000 } },
    "schema-agreement": { "timeout-ms": 10000 }
  },
  "reconnection-policy": { "type": "exponential", "base-ms": 1000, "max-ms": 600000 },
  "retry-policy": { "type": "standard-error-aware" },
  "load-balancing-policy": {
    "type": "token-aware", "token-aware": true, "shuffle": false,
    "dc-failover": false, "latency-awareness": false
  },
  "node-location-preference": { "type": "dc-auto" },
  "connection-pool": {
    "type": "host", "desired-connections-count": 1,
    "connection": { "max-requests": 1024 },
    "shard-aware": { "enabled": true }
  },
  "query-defaults": {
    "page": { "size": 5000 }, "consistency": "LOCAL_ONE", "serial-consistency": "SERIAL",
    "idempotence": false, "client-timestamps": true, "request": { "timeout-ms": 12000 }
  },
  "tls": { "enabled": false, "hostname-verification": false }
}

Follow-up

  • Confirm the server prerequisite before flipping the flag default on (unknown STARTUP key tolerance + client_options value-length — validated above for 2026.1.0).

Fixes DRIVER-382

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bf89edf6-2d85-4fbf-b834-aa3db2cacf6d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

nikagra added a commit to nikagra/java-driver that referenced this pull request Jul 30, 2026
…stage 1)

Stage 1 (groundwork) of client configuration reporting for the 3.x
driver -- the 3.x counterpart of the 4.x feature (DRIVER-381/scylladb#967).
Lets the driver report its effective configuration to ScyllaDB at
connection time via new STARTUP options, so operators can inspect
driver settings (system.clients.client_options) while investigating
incidents.

Gated behind Cluster.builder().withDriverConfigReporting(boolean),
which ships disabled (zero change on the wire when off). When enabled:

- SESSION_ID: a dedicated, driver-generated per-session UUID, sent on
  every connection (control and pool) so the server can group all of
  a session's connections. Independent of the user-settable CLIENT_ID.
- DRIVER_CONFIG: a compact JSON blob, sent only on the control
  connection. Stage 1 emits only {"version":1}; the full report
  follows in stage 2 (scylladb#974).

Reporting is fail-safe: any failure while building the report is
swallowed and never breaks connection initialization.

New DriverConfigReporter / DefaultDriverConfigReporter (package
com.datastax.driver.core), mirroring the existing ApplicationInfo
pattern, invoked from Connection.onOptionsReady(). The control
connection is marked via a new reportConfig flag threaded from
ControlConnection.tryConnect through a new
Connection.Factory.open(host, reportConfig) overload -- 3.x has no
pre-existing signal identifying the control connection at STARTUP
time.

Fixes DRIVER-382
nikagra added a commit to nikagra/java-driver that referenced this pull request Jul 30, 2026
…stage 1)

Stage 1 (groundwork) of client configuration reporting for the 3.x
driver -- the 3.x counterpart of the 4.x feature (DRIVER-381/scylladb#967).
Lets the driver report its effective configuration to ScyllaDB at
connection time via new STARTUP options, so operators can inspect
driver settings (system.clients.client_options) while investigating
incidents.

Gated behind Cluster.builder().withDriverConfigReporting(boolean),
which ships disabled (zero change on the wire when off). When enabled:

- SESSION_ID: a dedicated, driver-generated per-session UUID, sent on
  every connection (control and pool) so the server can group all of
  a session's connections. Independent of the user-settable CLIENT_ID.
- DRIVER_CONFIG: a compact JSON blob, sent only on the control
  connection. Stage 1 emits only {"version":1}; the full report
  follows in stage 2 (scylladb#974).

Reporting is fail-safe: any failure while building the report is
swallowed and never breaks connection initialization.

New DriverConfigReporter / DefaultDriverConfigReporter (package
com.datastax.driver.core), mirroring the existing ApplicationInfo
pattern, invoked from Connection.onOptionsReady(). The control
connection is marked via a new reportConfig flag threaded from
ControlConnection.tryConnect through a new
Connection.Factory.open(host, reportConfig) overload -- 3.x has no
pre-existing signal identifying the control connection at STARTUP
time.

Fixes DRIVER-382
@nikagra
nikagra force-pushed the feature/driver-config-reporting-3x-phase2 branch from d00e683 to 12571f1 Compare July 30, 2026 20:40
nikagra added a commit to nikagra/java-driver that referenced this pull request Jul 30, 2026
…stage 1)

Stage 1 (groundwork) of driver configuration reporting for the 3.x
driver -- the 3.x counterpart of the 4.x feature (DRIVER-381/scylladb#967).
Lets the driver report its effective configuration to ScyllaDB at
connection time via new STARTUP options, so operators can inspect
driver settings (system.clients.client_options) while investigating
incidents.

Gated behind Cluster.builder().withDriverConfigReporting(boolean),
which ships disabled (zero change on the wire when off). When enabled:

- SESSION_ID: a dedicated, driver-generated UUID, sent on every
  connection (control and pool) so the server can group all of a
  Cluster's connections -- including across multiple Sessions obtained
  from the same Cluster, since the control connection has no
  affiliation with any single Session. Independent of the
  user-settable CLIENT_ID.
- DRIVER_CONFIG: a compact JSON blob, sent only on the control
  connection. Stage 1 emits only {"version":1}; the full report
  follows in stage 2 (scylladb#974).

Reporting is fail-safe: any failure while building the report is
swallowed and never breaks connection initialization.

New DriverConfigReporter / DefaultDriverConfigReporter (package
com.datastax.driver.core), mirroring the existing ApplicationInfo
pattern, invoked from Connection.onOptionsReady(). Only constructed
when reporting is enabled -- a new NoopDriverConfigReporter is used
otherwise, so Jackson (used to build the JSON blob) is never loaded
when the feature is off; jackson-core/jackson-databind are marked
optional in driver-core/pom.xml accordingly. The control connection is
marked via a new reportConfig flag threaded from
ControlConnection.tryConnect through a new
Connection.Factory.open(host, reportConfig) overload -- 3.x has no
pre-existing signal identifying the control connection at STARTUP
time.

system.clients.client_options is per node, so DRIVER_CONFIG only
appears on the node holding the control connection.

Fixes DRIVER-382
@nikagra
nikagra force-pushed the feature/driver-config-reporting-3x-phase2 branch from 12571f1 to 4ef72fd Compare July 30, 2026 20:54
nikagra added a commit to nikagra/java-driver that referenced this pull request Jul 30, 2026
…stage 1)

Stage 1 (groundwork) of driver configuration reporting for the 3.x
driver -- the 3.x counterpart of the 4.x feature (DRIVER-381/scylladb#967).
Lets the driver report its effective configuration to ScyllaDB at
connection time via new STARTUP options, so operators can inspect
driver settings (system.clients.client_options) while investigating
incidents.

Gated behind Cluster.builder().withDriverConfigReporting(boolean),
which ships disabled (zero change on the wire when off). When enabled:

- SESSION_ID: a dedicated, driver-generated UUID, sent on every
  connection (control and pool) so the server can group all of a
  Cluster's connections -- including across multiple Sessions obtained
  from the same Cluster, since the control connection has no
  affiliation with any single Session. Independent of the
  user-settable CLIENT_ID.
- DRIVER_CONFIG: a compact JSON blob, sent only on the control
  connection. Stage 1 emits only {"version":1}; the full report
  follows in stage 2 (scylladb#974).

Reporting is fail-safe: any failure while building the report is
swallowed and never breaks connection initialization.

New DriverConfigReporter / DefaultDriverConfigReporter (package
com.datastax.driver.core), mirroring the existing ApplicationInfo
pattern, invoked from Connection.onOptionsReady(). Only constructed
when reporting is enabled -- a new NoopDriverConfigReporter is used
otherwise, so Jackson (used to build the JSON blob) is never loaded
when the feature is off; jackson-core/jackson-databind are marked
optional in driver-core/pom.xml accordingly. The control connection is
marked via a new reportConfig flag threaded from
ControlConnection.tryConnect through a new
Connection.Factory.open(host, reportConfig) overload -- 3.x has no
pre-existing signal identifying the control connection at STARTUP
time.

system.clients.client_options is per node, so DRIVER_CONFIG only
appears on the node holding the control connection.

Fixes DRIVER-382
@nikagra
nikagra force-pushed the feature/driver-config-reporting-3x-phase2 branch from 4ef72fd to 2120a94 Compare July 30, 2026 23:53
nikagra and others added 2 commits July 31, 2026 18:57
…stage 1)

Stage 1 (groundwork) of driver configuration reporting for the 3.x
driver -- the 3.x counterpart of the 4.x feature (DRIVER-381/scylladb#967).
Lets the driver report its effective configuration to ScyllaDB at
connection time via new STARTUP options, so operators can inspect
driver settings (system.clients.client_options) while investigating
incidents.

Two STARTUP options are added:

- SESSION_ID: a dedicated, driver-generated UUID sent on every
  connection (control and pool) unconditionally, like DRIVER_NAME and
  DRIVER_VERSION, so the server can group all of a Cluster's
  connections -- including across multiple Sessions obtained from the
  same Cluster, since the control connection has no affiliation with
  any single Session. Independent of the user-settable CLIENT_ID.
- DRIVER_CONFIG: a compact JSON blob describing the effective
  configuration, sent only on the control connection. Stage 1 emits
  only {"version":1}; the full report follows in stage 2 (scylladb#974).
  Enabled by default; opt out with
  Cluster.builder().withDriverConfigReporting(false).

The report is built once, while the Cluster initializes, and the
resulting string is reused for every control connection that Cluster
opens -- it is never rebuilt while the session is in flight. Building
it is fail-safe: any failure is swallowed and simply leaves
DRIVER_CONFIG unset instead of breaking cluster initialization.

New DriverConfigReporter / DefaultDriverConfigReporter (package
com.datastax.driver.core) build the blob. Connection.Factory, of which
there is one per Cluster, holds that Cluster's session id and the built
report, and hands the report to the control connection as a constructor
argument -- null everywhere else, which is what suppresses reporting.
The control connection is identified by threading a reportConfig flag
from ControlConnection.tryConnect through a new
Connection.Factory.open(host, reportConfig) overload, since 3.x has no
pre-existing signal identifying the control connection at STARTUP time.

jackson-core/jackson-databind are enforced as plain required
dependencies (as they already were in released 3.11.5.17), used to
build the JSON blob; the orphaned jackson-dataformat-yaml dependency
(dead since the Scylla Cloud config code was removed) is dropped, so
consumers no longer inherit SnakeYAML.

system.clients.client_options is per node, so DRIVER_CONFIG only
appears on the node holding the control connection.

Fixes DRIVER-382

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fills in the full DRIVER_CONFIG JSON report in the approved v2
cross-driver schema shape, replacing the stage-1 {"version":1}
placeholder. All groups are populated from Configuration and Policies
when the report is built, i.e. once per Cluster as it initializes.

Adds public getters to DCAwareRoundRobinPolicy and
RackAwareRoundRobinPolicy needed to report node-location-preference
and dc-failover, and makes PagingOptimizingLoadBalancingPolicy
implement ChainableLoadBalancingPolicy so the reporter can unwrap the
LB policy Cluster.Manager wraps at runtime.

Adds a JSON-Schema conformance test suite (mirroring the 4.x sibling
PR scylladb#968): the normative schema is shipped as a test resource and
validated via com.networknt:json-schema-validator (pinned to 1.5.x,
the last line still targeting Java 8), covering every discriminated-
union branch and optional group the 3.x reporter can emit, plus a
negative test proving additionalProperties=false is enforced.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant