Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxjava</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1175,16 +1175,26 @@ public enum DefaultDriverOption implements DriverOption {
*/
ADDRESS_TRANSLATOR_RESOLVE_ADDRESSES("advanced.address-translator.resolve-addresses"),
/**
* Whether the driver reports its effective configuration to ScyllaDB at connection time.
*
* <p>When {@code true}, the driver adds two entries to the CQL {@code STARTUP} options, which
* ScyllaDB stores in {@code system.clients.client_options} so operators can inspect driver
* settings while investigating incidents: a {@code SESSION_ID} on every connection (so the server
* can group a session's connections) and a compact JSON payload under the {@code DRIVER_CONFIG}
* key on the control connection only. At this stage the {@code DRIVER_CONFIG} payload carries
* only schema-version metadata (<code>{"version":1}</code>); reporting of the effective
* configuration fields is planned for a later stage. When {@code false}, neither entry is sent
* and there is no change on the wire.
* Whether the driver reports its effective configuration to the cluster at connection time.
* Defaults to {@code true}.
*
* <p>When {@code true}, the control connection adds a compact JSON payload under the {@code
* DRIVER_CONFIG} key to its CQL {@code STARTUP} options, which the server stores in its
* client-connection system table ({@code system.clients} on ScyllaDB, {@code
* system_views.clients} on Cassandra 4.1+) so operators can inspect driver settings while
* investigating incidents. It describes the effective configuration of the driver's default
* execution profile (connection/socket settings, timeouts, retry/reconnection/
* speculative-execution/load-balancing policies, connection pooling, query defaults, and TLS).
* Only the control connection sends it, since it describes the whole session. When {@code false},
* {@code DRIVER_CONFIG} is not sent.
*
* <p>This option governs {@code DRIVER_CONFIG} only. The {@code SESSION_ID} startup option, which
* lets the server group all of a session's connections, is an innate driver behavior: it is sent
* on every connection unconditionally, whatever this is set to. So turning reporting off is not
* the same as leaving the wire unchanged.
*
* <p>Reporting is best-effort: if the report cannot be built, or would exceed 32 KiB, it is
* skipped (with a warning) rather than allowed to interfere with connecting.
*
* <p>Value type: boolean
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ protected static void fillWithDriverDefaults(OptionsMap map) {
// values) with no sensible scalar default, analogous to how CONFIG_RELOAD_INTERVAL is omitted.
map.put(TypedDriverOption.CLIENT_ROUTES_NATIVE_TRANSPORT_PORT, 9042);
map.put(TypedDriverOption.CLIENT_ROUTES_SHARD_AWARENESS_ENABLED, false);
map.put(TypedDriverOption.DRIVER_CONFIG_REPORTING_ENABLED, false);
map.put(TypedDriverOption.DRIVER_CONFIG_REPORTING_ENABLED, true);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

@Immutable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ public String toString() {
new TypedDriverOption<>(
DefaultDriverOption.CLIENT_ROUTES_SHARD_AWARENESS_ENABLED, GenericType.BOOLEAN);

/** Whether the driver reports its configuration to ScyllaDB at connection time. */
/** Whether the driver reports its configuration to the cluster at connection time. */
public static final TypedDriverOption<Boolean> DRIVER_CONFIG_REPORTING_ENABLED =
new TypedDriverOption<>(
DefaultDriverOption.DRIVER_CONFIG_REPORTING_ENABLED, GenericType.BOOLEAN);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,20 @@ public SSLEngine newSslEngine(@NonNull EndPoint remoteEndpoint) {
return engine;
}

/**
* Whether {@link #newSslEngine} configures the engine to validate the server certificate against
* the node's host name, as passed to the constructor.
*
* <p>A diagnostic accessor, read by the driver-configuration report sent to the server at
* connection time. Deliberately not on {@link SslEngineFactory}: an arbitrary factory can neither
* be assumed to validate host names nor be assumed not to, and a default answer on the interface
* would misdescribe a security control for every implementation that never considered the
* question. The report names the factories it recognizes and says nothing about the rest.
*/
public boolean isHostnameValidationRequired() {
return requireHostnameValidation;
}

@Override
public void close() {
// nothing to do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@ public class ChannelFactory {
public static final String INFLIGHT_HANDLER_NAME = "inflight";
public static final String INIT_HANDLER_NAME = "init";

/**
* The number of orphaned requests a connection is actually built with, which is not always the
* configured {@code advanced.connection.max-orphan-requests}: that option has to stay below
* {@code advanced.connection.max-requests-per-connection}, and a value that does not is silently
* corrected to a quarter of it (the caller logs a warning when that happens).
*
* <p>Shared with {@code DefaultDriverConfigReporter}, which reports this number as {@code
* connection.requests.orphaned.max}: one implementation means the report cannot claim a limit the
* connection was not built with.
*
* @param maxRequestsPerConnection the configured {@code max-requests-per-connection}.
* @param maxOrphanRequests the configured {@code max-orphan-requests}.
*/
public static int effectiveMaxOrphanRequests(
int maxRequestsPerConnection, int maxOrphanRequests) {
return (maxOrphanRequests >= maxRequestsPerConnection)
? maxRequestsPerConnection / 4
: maxOrphanRequests;
}

private final String logPrefix;
protected final InternalDriverContext context;

Expand Down Expand Up @@ -377,20 +397,21 @@ protected void initChannel(Channel channel) {
(int) defaultConfig.getBytes(DefaultDriverOption.PROTOCOL_MAX_FRAME_LENGTH);
int maxRequestsPerConnection =
defaultConfig.getInt(DefaultDriverOption.CONNECTION_MAX_REQUESTS);
int maxOrphanRequests =
int configuredMaxOrphanRequests =
defaultConfig.getInt(DefaultDriverOption.CONNECTION_MAX_ORPHAN_REQUESTS);
if (maxOrphanRequests >= maxRequestsPerConnection) {
int maxOrphanRequests =
effectiveMaxOrphanRequests(maxRequestsPerConnection, configuredMaxOrphanRequests);
if (configuredMaxOrphanRequests >= maxRequestsPerConnection) {
if (LOGGED_ORPHAN_WARNING.compareAndSet(false, true)) {
LOG.warn(
"[{}] Invalid value for {}: {}. It must be lower than {}. "
+ "Defaulting to {} (1/4 of max-requests) instead.",
logPrefix,
DefaultDriverOption.CONNECTION_MAX_ORPHAN_REQUESTS.getPath(),
maxOrphanRequests,
configuredMaxOrphanRequests,
DefaultDriverOption.CONNECTION_MAX_REQUESTS.getPath(),
maxRequestsPerConnection / 4);
maxOrphanRequests);
}
maxOrphanRequests = maxRequestsPerConnection / 4;
}

InFlightHandler inFlightHandler =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,7 @@ public int getShardId() {
}

public ShardingInfo getShardingInfo() {
ConnectionShardingInfo info = getSupportedFeatures().getShardingInfo();
return info != null ? info.shardingInfo : null;
return getSupportedFeatures().getNodeShardingInfo();
}

public LwtInfo getLwtInfo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,14 @@ Message getRequest() {
return request = Options.INSTANCE;
case STARTUP:
Map<String, String> startupOptions = new HashMap<>(context.getStartupOptions());
if (featureStore != null) {
featureStore.populateStartupOptions(startupOptions);
featureStore.populateStartupOptions(startupOptions);
// The DRIVER_CONFIG blob describes the whole session, so only the control connection
// carries it (options.reportConfig); the other connections are correlated to it by the
// SESSION_ID that every connection already carries from context.getStartupOptions().
// No-op when driver config reporting is disabled.
if (options.reportConfig) {
context.getDriverConfigReporter().populateControlConnectionOptions(startupOptions);
}
// Adds SESSION_ID on every connection and DRIVER_CONFIG on the control connection
// (options.reportConfig); no-op when driver config reporting is disabled.
context
.getDriverConfigReporter()
.populateStartupOptions(startupOptions, options.reportConfig);
return request = new Startup(startupOptions);
case GET_CLUSTER_NAME:
return request = CLUSTER_NAME_QUERY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class ConstantReconnectionPolicy implements ReconnectionPolicy {
private static final Logger LOG = LoggerFactory.getLogger(ConstantReconnectionPolicy.class);

private final String logPrefix;
private final Duration delay;
private final ReconnectionSchedule schedule;

/** Builds a new instance. */
Expand All @@ -61,12 +62,24 @@ public ConstantReconnectionPolicy(DriverContext context) {
String.format(
"Invalid negative delay for "
+ DefaultDriverOption.RECONNECTION_BASE_DELAY.getPath()
+ " (got %d)",
+ " (got %s)",
delay));
}
this.delay = delay;
this.schedule = () -> delay;
}

/**
* The fixed delay between reconnection attempts that this instance was built with.
*
* <p>Read from the configuration once, at construction: a later configuration reload does not
* affect an already-running policy. Exposed so that diagnostics can describe the delay actually
* in force rather than whatever the profile currently says.
*/
public Duration getDelay() {
return delay;
}

@NonNull
@Override
public ReconnectionSchedule newNodeSchedule(@NonNull Node node) {
Expand Down
Loading
Loading