Class DefaultDriverConfigReporter

  • All Implemented Interfaces:
    DriverConfigReporter

    @Beta
    public class DefaultDriverConfigReporter
    extends Object
    implements DriverConfigReporter
    Default DriverConfigReporter: serializes the driver configuration to the cross-driver DRIVER_CONFIG JSON shape, which Connection.Factory then sends in the control connection's STARTUP options.

    The report is built afresh for every control connection, from inside its STARTUP frame assembly, and never cached: it describes the objects that are in force at that handshake rather than the configuration the Cluster was constructed from. That distinction is not theoretical. Cluster.Manager.init() builds the Connection.Factory before it calls LoadBalancingPolicy.init(com.datastax.driver.core.Cluster, java.util.Collection<com.datastax.driver.core.Host>), and the first control connection's STARTUP goes out in between — so a datacenter or rack the policy infers from the node it reaches cannot be known on the first report and is known on every later one. A reconnect therefore costs one report build and may legitimately report more than the connection before it did.

    The report follows the normative cross-driver schema (report version 1): kebab-case keys, nested objects, and omission of any key or group that has no value (nothing is ever emitted as null). The same applies where a configured value falls outside what the schema can express but the key is optional: a disabled connect timeout, a disabled read timeout, a disabled SO_LINGER, an unbounded page size and a non-serial default serial consistency level are omitted rather than emitted as a value the schema rejects. Two keys are omitted for a third reason — the schema admits only a boolean, and 3.x cannot observe which one applies: query.defaults.client-timestamps for a timestamp generator that is none of the driver's own, and connection.tls.hostname-verification for any SSLOptions other than SniSSLOptions. Both keys are documented as absent when the behavior is unknown, which is exactly the case here: a custom generator decides per call whether to assign a timestamp at all, and every other SSLOptions builds its engine from a user-supplied SSLContext (or hands the whole handler over to Netty), so nothing the reporter can read says whether the hostname is checked. Two more optional bounds are omitted for the opposite reason — 3.x simply has no such bound to report: connection.reconnection.policy.max-attempts, since its reconnection policies retry forever, and query.retry.policy.max-retries, since no single number describes a 3.x retry policy: RetryPolicy.onRequestError(com.datastax.driver.core.Statement, com.datastax.driver.core.ConsistencyLevel, com.datastax.driver.core.exceptions.DriverException, int) is the one path that does not stop after a single retry, and it is only reached for an idempotent statement, so the same policy bounds a non-idempotent request at one retry and an idempotent one at the length of the query plan. Which of the two a statement gets is not configuration — Statement.setIdempotent(boolean) overrides the reported query.defaults.idempotence per statement.

    Only a token-aware chain has a built-in shape in the schema, whose query.load-balancing.policy.type admits token-aware and custom and nothing else. That shape carries no name, so it is claimed only for a chain every element of which the group can describe; any other chain is custom, named after every policy in it — WhiteListPolicy(TokenAwarePolicy(DCAwareRoundRobinPolicy)) — and keeps the capability keys it can still state, which the custom branch admits as additional properties. See loadBalancingPolicy(java.util.List<com.datastax.driver.core.policies.LoadBalancingPolicy>, com.datastax.driver.core.policies.LoadBalancingPolicy, com.datastax.driver.core.DefaultDriverConfigReporter.NodeLocation). A datacenter and rack are reported in query.load-balancing.node-preference whenever the chain prefers one: from a DC- or rack-aware policy, or from a filter restricting the session to a single datacenter. A bare RoundRobinPolicy, or a WhiteListPolicy over one, prefers neither and both preference keys are omitted.

    The schema reports that preference in a second, optional place, connection.node-preference, which describes the part of the cluster the driver holds connections to rather than how a query is routed. One LoadBalancingPolicy decides both in 3.x, since LoadBalancingPolicy.distance(Host) is what governs whether a host is pooled at all, so that key is derived from the same chain — but from its datacenter half alone. A rack-aware policy's distance() returns REMOTE, never IGNORED, for a local-datacenter host in another rack, so those hosts are still pooled and the rack does not scope what the driver connects to. The datacenter does: a host outside the preferred one is IGNORED unless the policy is configured to use hosts there, and an ignored host gets no pool at all.

    That last claim is exact only for the policy the preference was read from. A chainable policy above it can narrow pooling further: HostFilterPolicy.distance(Host) — and therefore WhiteListPolicy's — returns IGNORED for any host failing its predicate, including one inside the reported datacenter, and a custom policy computes distance() itself and need honor nothing the chain below it prefers. The reported datacenter is thus necessary but not sufficient: it is what the located policy prefers, not the whole of what gets a pool. Reported all the same, deliberately, on the grounds that hiding a datacenter the operator really did configure is the worse failure mode — note the asymmetry with the inferred values above, in that nothing is inferred on a third party's behalf while what was explicitly configured is passed through even where a wrapper may override it.

    A filter that restricts the session to exactly one datacenter is read the other way round: HostFilterPolicy.fromDCWhiteList(LoadBalancingPolicy, Iterable) retains the datacenters it was given, so when nothing in the chain prefers a datacenter of its own — fromDCWhiteList(new RoundRobinPolicy(), ["dc1"]) — the restriction is what decides the preference and is reported as one. Anything a filter can express that names no single datacenter keeps the group omitted: a blacklist, several allowed datacenters, a blank name, a WhiteListPolicy (which filters on addresses), or a caller-supplied Predicate<Host>, which stays opaque. A filter the preference was not read from — because a location-aware policy below it won — narrows the chain without being stated anywhere, so it costs the chain its built-in shape and is named instead, exactly like any other restricting wrapper.

    Known limitations: omission is not always available, because these fields are required by the schema.

    connection.requests.orphaned.max used to belong to that list and no longer does: 3.x has no orphaned-request setting to report it from — a request the driver stopped waiting for keeps its stream identifier until the response arrives, with no configurable bound and no connection replacement (only 4.x has advanced.connection.max-orphan-requests) — and the schema now makes the group optional for exactly that case, so omission is the schema-valid answer.

    Such a value is reported as-is, and a key with no equivalent stays omitted: the reporter deliberately neither fabricates an in-range value — which would misreport a setting an operator may have chosen on purpose, or a policy 3.x does not implement — nor drops the whole report over one field, so the document is accurate but fails schema validation. Tracked as a cross-driver schema gap: the fix is to let those fields express the value, the way control-plane.schema.agreement.timeout-ms already admits 0.

    The read timeout feeds three places, all of them optional, so disabling it omits all three: connection.read, control-plane.queries.system.timeout.client-side-ms and query.defaults.request.

    • Field Detail

      • DRIVER_CONFIG_KEY

        public static final String DRIVER_CONFIG_KEY
        STARTUP option key under which the config JSON is sent.
        See Also:
        Constant Field Values
    • Constructor Detail

      • DefaultDriverConfigReporter

        public DefaultDriverConfigReporter​(Configuration configuration)
    • Method Detail

      • buildReport

        public String buildReport()
        Description copied from interface: DriverConfigReporter
        Builds the configuration report, or returns null if it could not be built (in which case no DRIVER_CONFIG option is sent).

        Called once per control connection, from its STARTUP frame assembly, and not cached: the report describes the objects in force at the handshake that sends it, rather than the configuration the Cluster was constructed from. Implementations must therefore tolerate being called on a Netty event loop, and repeatedly over a cluster's lifetime.

        Implementations must not throw: a failure to build the report must be swallowed (and logged) rather than propagated, so that a diagnostic aid can never break cluster initialization. For the same reason they must bound the report's size and return null rather than an oversized one: STARTUP option values carry an unchecked 16-bit length prefix, so a value over 65535 encoded bytes corrupts the frame instead of merely being useless. The built-in reporter caps the report at 32KiB of UTF-8, the limit the other ScyllaDB drivers apply.

        An implementation that cannot even be loaded is handled one level up rather than here: DefaultDriverConfigReporter needs Jackson, and on a classpath without it the failure is a LinkageError raised while initializing the class, which no method of this interface could catch. Connection.Factory.buildDriverConfigReport contains that and reports nothing, so the connection is still established.

        Specified by:
        buildReport in interface DriverConfigReporter
        Returns:
        the report to send under the DRIVER_CONFIG startup option, or null to send nothing.
      • buildJson

        protected String buildJson()
        Builds the compact, single-line JSON configuration report.
      • populateConfig

        protected void populateConfig​(ObjectNode root)
        Populates the three top-level configuration groups onto the report root from configuration and its policies. Keys the driver has no equivalent for (or cannot introspect in 3.x) are omitted rather than emitted as null.