-
Notifications
You must be signed in to change notification settings - Fork 4
Add support for miscellaneous client options #424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -39,6 +39,10 @@ const { ExecutionProfile } = require("./execution-profile.js"); | |||||||||||||||||
| * but it is usually a good idea to provide more than one contact point, because if that single contact point is | ||||||||||||||||||
| * unavailable, the driver will not be able to initialize correctly. | ||||||||||||||||||
| * | ||||||||||||||||||
| * The port can be specified either per contact point (e.g. `'10.0.1.1:9043'`) or globally via | ||||||||||||||||||
| * `protocolOptions.port`, but not both. If `protocolOptions.port` is set, contact points must not | ||||||||||||||||||
| * include a port. | ||||||||||||||||||
| * | ||||||||||||||||||
| * @property {String} [localDataCenter] The local data center to use. | ||||||||||||||||||
| * | ||||||||||||||||||
| * If using DCAwareRoundRobinPolicy (default), this option is required and only hosts from this data center are | ||||||||||||||||||
|
|
@@ -68,7 +72,6 @@ const { ExecutionProfile } = require("./execution-profile.js"); | |||||||||||||||||
| * This value is passed to database and is useful as metadata for describing a client connection on the server side. | ||||||||||||||||||
| * @property {Number} [refreshSchemaDelay] The default window size in milliseconds used to debounce node list and schema | ||||||||||||||||||
| * refresh metadata requests. Default: 1000. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Boolean} [isMetadataSyncEnabled] Determines whether client-side schema metadata retrieval and update is | ||||||||||||||||||
| * enabled. | ||||||||||||||||||
| * | ||||||||||||||||||
|
|
@@ -117,10 +120,9 @@ const { ExecutionProfile } = require("./execution-profile.js"); | |||||||||||||||||
| * @property {QueryOptions} [queryOptions] Default options for all queries. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Object} [pooling] Pooling options. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [pooling.heartBeatInterval] The amount of idle time in milliseconds that has to pass before the | ||||||||||||||||||
| * driver issues a request on an active connection to avoid idle time disconnections. Default: 30000. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * driver issues a request on an active connection to avoid idle time disconnections. | ||||||||||||||||||
| * Note: this configures CQL-layer keepalives. See also: keepAliveDelay. Default: 30000. | ||||||||||||||||||
| * @property {Object} [pooling.coreConnectionsPerHost] Associative array containing amount of connections per host | ||||||||||||||||||
| * distance. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
|
|
@@ -134,28 +136,25 @@ const { ExecutionProfile } = require("./execution-profile.js"); | |||||||||||||||||
| * connect. Default: true. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Object} [protocolOptions] | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [protocolOptions.port] The port to use to connect to the Cassandra host. If not set through this | ||||||||||||||||||
| * method, the default port (9042) will be used instead. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [protocolOptions.port] The port to use to connect to the Cassandra host. If not set, the | ||||||||||||||||||
| * default port (9042) will be used instead. | ||||||||||||||||||
| * | ||||||||||||||||||
| * When set, the port is appended to all contact point addresses. The port can be specified either | ||||||||||||||||||
| * here or per contact point (e.g. `'10.0.1.1:9043'`), but not both. | ||||||||||||||||||
| * @property {Number} [protocolOptions.maxSchemaAgreementWaitSeconds] The maximum time in seconds to wait for schema | ||||||||||||||||||
| * agreement between nodes before returning from a DDL query. Default: 10. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [protocolOptions.maxVersion] When set, it limits the maximum protocol version used to connect to | ||||||||||||||||||
| * the nodes. | ||||||||||||||||||
| * Useful for using the driver against a cluster that contains nodes with different major/minor versions of Cassandra. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Object} [socketOptions] | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [socketOptions.connectTimeout] Connection timeout in milliseconds. Default: 5000. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [socketOptions.connectTimeout] Connection timeout in milliseconds. If it's higher than the underlying | ||||||||||||||||||
| * OS's default connection timeout it won't take effect. Default: 5000. | ||||||||||||||||||
| * @property {Number} [socketOptions.defunctReadTimeoutThreshold] Determines the amount of requests that simultaneously | ||||||||||||||||||
| * have to timeout before closing the connection. Default: 64. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Boolean} [socketOptions.keepAlive] Whether to enable TCP keep-alive on the socket. Default: true. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [socketOptions.keepAliveDelay] TCP keep-alive delay in milliseconds. Default: 0. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [socketOptions.readTimeout] Per-host read timeout in milliseconds. | ||||||||||||||||||
| * | ||||||||||||||||||
| * Please note that this is not the maximum time a call to {@link Client#execute} may have to wait; | ||||||||||||||||||
|
|
@@ -173,7 +172,6 @@ const { ExecutionProfile } = require("./execution-profile.js"); | |||||||||||||||||
| * Setting a value of 0 disables read timeouts. Default: `12000`. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Boolean} [socketOptions.tcpNoDelay] When set to true, it disables the Nagle algorithm. Default: true. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Number} [socketOptions.coalescingThreshold] Buffer length in bytes use by the write queue before flushing | ||||||||||||||||||
| * the frames. Default: 8000. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
|
|
@@ -186,15 +184,11 @@ const { ExecutionProfile } = require("./execution-profile.js"); | |||||||||||||||||
| * You can specify cert, ca, ... options named after the Node.js `tls.connect()` options. | ||||||||||||||||||
| * | ||||||||||||||||||
| * It uses the same default values as Node.js `tls.connect()` | ||||||||||||||||||
| * [TODO: For now, only limited subset of ssl options is supported] | ||||||||||||||||||
| * @property {Object} [encoding] Encoding options. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Function} [encoding.map] Map constructor to use for Cassandra map<k,v> type encoding and decoding. | ||||||||||||||||||
| * If not set, it will default to Javascript Object with map keys as property names. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Function} [encoding.set] Set constructor to use for Cassandra set<k> type encoding and decoding. | ||||||||||||||||||
| * If not set, it will default to Javascript Array. | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Boolean} [encoding.copyBuffer] Determines if the network buffer should be copied for buffer based data | ||||||||||||||||||
| * types (blob, uuid, timeuuid and inet). | ||||||||||||||||||
| * | ||||||||||||||||||
|
|
@@ -206,8 +200,6 @@ const { ExecutionProfile } = require("./execution-profile.js"); | |||||||||||||||||
| * Setting it to false will cause less overhead and the reference of the network buffer to be maintained until the row | ||||||||||||||||||
| * / result set are de-referenced. | ||||||||||||||||||
| * Default: true. | ||||||||||||||||||
| * | ||||||||||||||||||
| * [TODO: Add support for this field] | ||||||||||||||||||
| * @property {Boolean} [encoding.useUndefinedAsUnset] Valid for Cassandra 2.2 and above. Determines that, if a parameter | ||||||||||||||||||
| * is set to `undefined` it should be encoded as `unset`. | ||||||||||||||||||
| * | ||||||||||||||||||
|
|
@@ -438,7 +430,6 @@ function defaultOptions() { | |||||||||||||||||
| paged: true, | ||||||||||||||||||
| }, | ||||||||||||||||||
| protocolOptions: { | ||||||||||||||||||
| port: 9042, | ||||||||||||||||||
| maxSchemaAgreementWaitSeconds: 10, | ||||||||||||||||||
| maxVersion: 0, | ||||||||||||||||||
| }, | ||||||||||||||||||
|
|
@@ -967,7 +958,41 @@ function setRustOptions(options) { | |||||||||||||||||
| rustOptions.sslOptions = normalizeSslOptions(options.sslOptions); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (options.socketOptions) { | ||||||||||||||||||
| if (options.socketOptions.connectTimeout != null) { | ||||||||||||||||||
| rustOptions.connectTimeoutMillis = | ||||||||||||||||||
| options.socketOptions.connectTimeout; | ||||||||||||||||||
| } | ||||||||||||||||||
| if (options.socketOptions.tcpNoDelay != null) { | ||||||||||||||||||
| rustOptions.tcpNodelay = options.socketOptions.tcpNoDelay; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (options.socketOptions.keepAlive === true) { | ||||||||||||||||||
| // keepAliveDelay is in milliseconds; 0 means use the OS default. | ||||||||||||||||||
| // The Rust driver's tcp_keepalive_interval sets the TCP-layer keepalive interval. | ||||||||||||||||||
| // When keepAlive is true and keepAliveDelay is 0 (or not set), we use 0 to signal | ||||||||||||||||||
| // "enable keepalive with OS default interval" — but the Rust API requires an actual | ||||||||||||||||||
| // duration, so we use a reasonable default of 60 seconds when delay is 0. | ||||||||||||||||||
| const delay = options.socketOptions.keepAliveDelay || 60000; | ||||||||||||||||||
| rustOptions.tcpKeepaliveIntervalMillis = delay; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (options.pooling && options.pooling.heartBeatInterval != null) { | ||||||||||||||||||
| if (options.pooling.heartBeatInterval > 0) { | ||||||||||||||||||
| rustOptions.keepaliveIntervalMillis = | ||||||||||||||||||
| options.pooling.heartBeatInterval; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (options.protocolOptions) { | ||||||||||||||||||
| if (options.protocolOptions.maxSchemaAgreementWaitSeconds != null) { | ||||||||||||||||||
| rustOptions.schemaAgreementTimeoutSecs = | ||||||||||||||||||
| options.protocolOptions.maxSchemaAgreementWaitSeconds; | ||||||||||||||||||
| } | ||||||||||||||||||
| if (options.protocolOptions.port != null) { | ||||||||||||||||||
|
||||||||||||||||||
| if (options.protocolOptions.port != null) { | |
| if ( | |
| options.protocolOptions.port != null && | |
| options.protocolOptions.port !== 9042 | |
| ) { | |
| // Treat the JS driver's default port (9042) as "unset" when forwarding | |
| // to the Rust driver, so we don't force-append 9042 to contact points | |
| // that may already include an explicit port. |
Copilot
AI
Mar 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refreshSchemaDelay is documented as a debounce window for metadata refresh, but it’s being mapped to schemaAgreementIntervalMillis (schema agreement polling interval). Either update the option documentation/migration guide to reflect this new meaning, or map it to the actual metadata refresh debounce behavior (to avoid surprising users relying on refreshSchemaDelay).
| rustOptions.schemaAgreementIntervalMillis = options.refreshSchemaDelay; | |
| throwNotSupported("Option refreshSchemaDelay"); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,7 @@ | ||||||||||||||||||||||||||||||
| use std::collections::HashMap; | ||||||||||||||||||||||||||||||
| use std::fmt::Debug; | ||||||||||||||||||||||||||||||
| use std::sync::Arc; | ||||||||||||||||||||||||||||||
| use std::time::Duration; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| use napi::bindgen_prelude::BigInt; | ||||||||||||||||||||||||||||||
| use openssl::pkcs12::Pkcs12; | ||||||||||||||||||||||||||||||
|
|
@@ -94,6 +95,13 @@ struct SessionOptions { | |||||||||||||||||||||||||||||
| load_balancing_config, loadBalancingConfig: LoadBalancingConfig, | ||||||||||||||||||||||||||||||
| retry_policy, retryPolicy: RetryPolicyKind, | ||||||||||||||||||||||||||||||
| address_translator_config, addressTranslatorConfig: FixedAddressTranslatorConfig, | ||||||||||||||||||||||||||||||
| connect_timeout_millis, connectTimeoutMillis: u32, | ||||||||||||||||||||||||||||||
| tcp_nodelay, tcpNodelay: bool, | ||||||||||||||||||||||||||||||
| tcp_keepalive_interval_millis, tcpKeepaliveIntervalMillis: u32, | ||||||||||||||||||||||||||||||
| keepalive_interval_millis, keepaliveIntervalMillis: u32, | ||||||||||||||||||||||||||||||
| schema_agreement_timeout_secs, schemaAgreementTimeoutSecs: u32, | ||||||||||||||||||||||||||||||
| schema_agreement_interval_millis, schemaAgreementIntervalMillis: u32, | ||||||||||||||||||||||||||||||
| default_port, defaultPort: u32, | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| impl Debug for SslOptions { | ||||||||||||||||||||||||||||||
|
|
@@ -268,12 +276,45 @@ fn configure_ssl(options: &SslOptions) -> ConvertedResult<Option<SslContext>> { | |||||||||||||||||||||||||||||
| Ok(Some(ssl_context_builder.build())) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| /// Appends the port to a contact point address. | ||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||
| /// The port can be specified either per contact point (e.g. `"10.0.1.1:9043"`) | ||||||||||||||||||||||||||||||
| /// or globally via `protocolOptions.port`, but not both. This function is only | ||||||||||||||||||||||||||||||
| /// called when `protocolOptions.port` is set, in which case it is appended to | ||||||||||||||||||||||||||||||
| /// all contact points. | ||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||
| /// IPv6 addresses are wrapped in brackets to produce the `[addr]:port` form | ||||||||||||||||||||||||||||||
| /// expected by the Rust driver. | ||||||||||||||||||||||||||||||
| fn append_port(cp: &str, port: u32) -> String { | ||||||||||||||||||||||||||||||
| // Bare IPv6 address (contains colons, e.g. "::1", "2001:db8::1") — wrap in brackets. | ||||||||||||||||||||||||||||||
| // This branch will also catch and properly handle IPv4 addresses. | ||||||||||||||||||||||||||||||
| if let Ok(ip) = cp.parse::<std::net::IpAddr>() { | ||||||||||||||||||||||||||||||
| return std::net::SocketAddr::new(ip, port as u16).to_string(); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+290
to
+294
|
||||||||||||||||||||||||||||||
| // This branch will also catch and properly handle IPv4 addresses. | |
| if let Ok(ip) = cp.parse::<std::net::IpAddr>() { | |
| return std::net::SocketAddr::new(ip, port as u16).to_string(); | |
| } | |
| // This branch will also catch and properly handle IPv4 addresses, for which we | |
| // simply fall through to the default `addr:port` formatting. | |
| if let Ok(ip) = cp.parse::<std::net::IpAddr>() { | |
| if matches!(ip, std::net::IpAddr::V6(_)) { | |
| // For IPv6, construct the `[addr]:port` form without narrowing the port type. | |
| return format!("[{}]:{}", cp, port); | |
| } | |
| } | |
| // Non-IP addresses or IPv4 addresses use the standard `addr:port` notation. |
Copilot
AI
Mar 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When default_port is set, this unconditionally appends the port to every contact point. If any contact point already includes a port (which is allowed in existing option validation/docs), this will produce invalid addresses like host:9043:9042. Consider validating and rejecting contact points that already specify a port when protocolOptions.port is set (or skipping appending when a port is already present).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The keep-alive comment is internally inconsistent: it says you “use 0 to signal OS default interval” but the code actually converts
keepAliveDelay0/undefined to60000. Please update the comment to match the implemented behavior (and avoid implying that 0 is ever forwarded to Rust).