/* * Copyright DataStax, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; const events = require('events'); const util = require('util'); const utils = require('./utils.js'); const errors = require('./errors.js'); const types = require('./types'); const { ProfileManager } = require('./execution-profile'); const requests = require('./requests'); const clientOptions = require('./client-options'); const ClientState = require('./metadata/client-state'); const description = require('../package.json').description; const { version } = require('../package.json'); const { DefaultExecutionOptions } = require('./execution-options'); const ControlConnection = require('./control-connection'); const RequestHandler = require('./request-handler'); const PrepareHandler = require('./prepare-handler'); const InsightsClient = require('./insights-client'); const cloud = require('./datastax/cloud'); const GraphExecutor = require('./datastax/graph/graph-executor'); const promiseUtils = require('./promise-utils'); /** * Max amount of pools being warmup in parallel, when warmup is enabled * @private */ const warmupLimit = 32; /** * Client options. *
While the driver provides lots of extensibility points and configurability, few client options are required.
*Default values for all settings are designed to be suitable for the majority of use cases, you should avoid * fine tuning it when not needed.
*See [Client constructor]{@link Client} documentation for recommended options.
* @typedef {Object} ClientOptions * @property {Array.* Contact points are addresses of Cassandra nodes that the driver uses to discover the cluster topology. *
** Only one contact point is required (the driver will retrieve the address of the other nodes automatically), * 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. *
* @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 * connected to and used in query plans. *
* @property {String} [keyspace] The logged keyspace for all the connections created within the {@link Client} instance. * @property {Object} [credentials] An object containing the username and password for plain-text authentication. * It configures the authentication provider to be used against Apache Cassandra's PasswordAuthenticator or DSE's * DseAuthenticator, when default auth scheme is plain-text. *
* Note that you should configure either credentials
or authProvider
to connect to an
* auth-enabled cluster, but not both.
*
This value is passed to DSE and is useful as metadata for describing a client connection on the server side.
* @property {String} [applicationVersion] An optional setting identifying the version of the application using * the {@link Client} instance. *This value is passed to DSE and is useful as metadata for describing a client connection on the server side.
* @property {Object} [monitorReporting] Options for reporting mechanism from the client to the DSE server, for * versions that support it. * @property {Boolean} [monitorReporting.enabled=true] Determines whether the reporting mechanism is enabled. * Defaults totrue
.
* @property {Object} [cloud] The options to connect to a cloud instance.
* @property {String|URL} cloud.secureConnectBundle Determines the file path for the credentials file bundle.
* @property {Number} [refreshSchemaDelay] The default window size in milliseconds used to debounce node list and schema
* refresh metadata requests. Default: 1000.
* @property {Boolean} [isMetadataSyncEnabled] Determines whether client-side schema metadata retrieval and update is
* enabled.
* Setting this value to false
will cause keyspace information not to be automatically loaded, affecting
* replica calculation per token in the different keyspaces. When disabling metadata synchronization, use
* [Metadata.refreshKeyspaces()]{@link module:metadata~Metadata#refreshKeyspaces} to keep keyspace information up to
* date or token-awareness will not work correctly.
true
.
* @property {Boolean} [prepareOnAllHosts] Determines if the driver should prepare queries on all hosts in the cluster.
* Default: true
.
* @property {Boolean} [rePrepareOnUp] Determines if the driver should re-prepare all cached prepared queries on a
* host when it marks it back up.
* Default: true
.
* @property {Number} [maxPrepared] Determines the maximum amount of different prepared queries before evicting items
* from the internal cache. Reaching a high threshold hints that the queries are not being reused, like when
* hard-coding parameter values inside the queries.
* Default: 500
.
* @property {Object} [policies]
* @property {LoadBalancingPolicy} [policies.loadBalancing] The load balancing policy instance to be used to determine
* the coordinator per query.
* @property {RetryPolicy} [policies.retry] The retry policy.
* @property {ReconnectionPolicy} [policies.reconnection] The reconnection policy to be used.
* @property {AddressTranslator} [policies.addressResolution] The address resolution policy.
* @property {SpeculativeExecutionPolicy} [policies.speculativeExecution] The SpeculativeExecutionPolicy
* instance to be used to determine if the client should send speculative queries when the selected host takes more
* time than expected.
*
* Default: [NoSpeculativeExecutionPolicy]{@link
* module:policies/speculativeExecution~NoSpeculativeExecutionPolicy}
*
* Default: [MonotonicTimestampGenerator]{@link module:policies/timestampGeneration~MonotonicTimestampGenerator}
*
*
Use null
to disable client-side timestamp generation.
* When this option is supplied SELECT
, UPDATE
, DELETE
, and BATCH
* statements on COMPACT STORAGE
tables function in "compatibility" mode which allows seeing these tables
* as if they were "regular" CQL tables.
*
* This option only effects interactions with interactions with tables using COMPACT STORAGE
and is only
* supported by C* 3.0.16+, 3.11.2+, 4.0+ and DSE 6.0+.
*
* Please note that this is not the maximum time a call to {@link Client#execute} may have to wait;
* this is the maximum time that call will wait for one particular Cassandra host, but other hosts will be tried if
* one of them timeout. In other words, a {@link Client#execute} call may theoretically wait up to
* readTimeout * number_of_cassandra_hosts
(though the total number of hosts tried for a given query also
* depends on the LoadBalancingPolicy in use).
*
When setting this value, keep in mind the following:
*12000
.
* @property {Boolean} [socketOptions.tcpNoDelay] When set to true, it disables the Nagle algorithm. Default: true.
* @property {Number} [socketOptions.coalescingThreshold] Buffer length in bytes use by the write queue before flushing
* the frames. Default: 8000.
* @property {AuthProvider} [authProvider] Provider to be used to authenticate to an auth-enabled cluster.
* @property {RequestTracker} [requestTracker] The instance of RequestTracker used to monitor or log requests executed
* with this instance.
* @property {Object} [sslOptions] Client-to-node ssl options. When set the driver will use the secure layer.
* 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()
except for rejectUnauthorized
* which is set to false
by default (for historical reasons). This setting is likely to change
* in upcoming versions to enable validation by default.
*
* Setting it to true will cause that the network buffer is copied for each row value of those types, * causing additional allocations but freeing the network buffer to be reused. * Setting it to true is a good choice for cases where the Row and ResultSet returned by the queries are long-lived * objects. *
** 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. *
* @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
.
*
* By default, ECMAScript undefined
is encoded as null
in the driver. Cassandra 2.2
* introduced the concept of unset.
* At driver level, you can set a parameter to unset using the field types.unset
. Setting this flag to
* true allows you to use ECMAScript undefined as Cassandra unset
.
*
* Default: true. *
* @property {Boolean} [encoding.useBigIntAsLong] Use [BigInt ECMAScript type](https://tc39.github.io/proposal-bigint/) * to represent CQL bigint and counter data types. * @property {Boolean} [encoding.useBigIntAsVarint] Use [BigInt ECMAScript * type](https://tc39.github.io/proposal-bigint/) to represent CQL varint data type. * @property {Array.Promise
from a
* callback-style function.
*
* Promise libraries often provide different methods to create a promise. For example, you can use Bluebird's
* Promise.fromCallback()
method.
*
* By default, the driver will use the * [Promise constructor]{@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise}. *
*/ /** * Query options * @typedef {Object} QueryOptions * @property {Boolean} [autoPage] Determines if the driver must retrieve the following result pages automatically. ** This setting is only considered by the [Client#eachRow()]{@link Client#eachRow} method. For more information, * check the * [paging results documentation]{@link https://docs.datastax.com/en/developer/nodejs-driver/latest/features/paging/}. *
* @property {Boolean} [captureStackTrace] Determines if the stack trace before the query execution should be * maintained. *
* Useful for debugging purposes, it should be set to false
under production environment as it adds an
* unnecessary overhead to each execution.
*
* Defaults to localOne
for Apache Cassandra and DSE deployments.
* For DataStax Astra, it defaults to localQuorum
.
*
When set, it executes as a different user/role than the one currently authenticated (a.k.a. proxy execution).
*This feature is only available in DSE 5.1+.
* @property {String|ExecutionProfile} [executionProfile] Name or instance of the [profile]{@link ExecutionProfile} to * be used for this execution. If not set, it will the use "default" execution profile. * @property {Number} [fetchSize] Amount of rows to retrieve per page. * @property {Array|ArrayFor batch queries, an array of such arrays, ordered as with the queries in the batch.
* @property {Host} [host] The host that should handle the query. ** Use of this option is heavily discouraged and should only be used in the following cases: *
*system
and system_views
* keyspaces.
* * Configuring a specific host causes the configured * [LoadBalancingPolicy]{@link module:policies/loadBalancing~LoadBalancingPolicy} to be completely bypassed. * However, if the load balancing policy dictates that the host is at a * [distance of ignored]{@link module:types~distance} or there is no active connectivity to the host, the request will * fail with a [NoHostAvailableError]{@link module:errors~NoHostAvailableError}. *
* @property {Boolean} [isIdempotent] Defines whether the query can be applied multiple times without changing the result * beyond the initial application. ** The query execution idempotence can be used at [RetryPolicy]{@link module:policies/retry~RetryPolicy} level to * determine if an statement can be retried in case of request error or write timeout. *
*Default: false
.
Useful for manual paging, if provided, the query will be executed starting from a given paging state.
* @property {Boolean} [prepare] Determines if the query must be executed as a prepared statement. * @property {Number} [readTimeout] When defined, it overrides the default read timeout * (socketOptions.readTimeout
) in milliseconds for this execution per coordinator.
* * Suitable for statements for which the coordinator may allow a longer server-side timeout, for example aggregation * queries. *
*
* A value of 0
disables client side read timeout for the execution. Default: undefined
.
*
* This property can be used to specify a different [retry policy]{@link module:policies/retry} to the one specified * in the {@link ClientOptions}.policies. *
* @property {Array} [routingIndexes] Index of the parameters that are part of the partition key to determine * the routing. * @property {Buffer|Array} [routingKey] Partition key(s) to determine which coordinator should be used for the query. * @property {Array} [routingNames] Array of the parameters names that are part of the partition key to determine the * routing. Only valid for non-prepared requests, it's recommended that you use the prepare flag instead. * @property {Number} [serialConsistency] Serial consistency is the consistency level for the serial phase of * conditional updates. * This option will be ignored for anything else that a conditional update/insert. * @property {Number|Long} [timestamp] The default timestamp for the query in microseconds from the unix epoch * (00:00:00, January 1st, 1970). *If provided, this will replace the server side assigned timestamp as default timestamp.
*Use [generateTimestamp()]{@link module:types~generateTimestamp} utility method to generate a valid timestamp * based on a Date and microseconds parts.
* @property {Boolean} [traceQuery] Enable query tracing for the execution. Use query tracing to diagnose performance * problems related to query executions. Default: false. *To retrieve trace, you can call [Metadata.getTrace()]{@link module:metadata~Metadata#getTrace} method.
* @property {Object} [graphOptions] Default options for graph query executions. ** These options are meant to provide defaults for all graph query executions. Consider using * [execution profiles]{@link ExecutionProfile} if you plan to reuse different set of options across different * query executions. *
* @property {String} [graphOptions.language] The graph language to use in graph queries. Default: *'gremlin-groovy'
.
* @property {String} [graphOptions.name] The graph name to be used in all graph queries.
* * This property is required but there is no default value for it. This value can be overridden at query level. *
* @property {Number} [graphOptions.readConsistency] Overrides the * [consistency level]{@link module:types~consistencies} * defined in the query options for graph read queries. * @property {Number} [graphOptions.readTimeout] Overrides the default per-host read timeout (in milliseconds) for all * graph queries. Default:0
.
*
* Use null
to reset the value and use the default on socketOptions.readTimeout
.
*
'g'
.
* @property {Number} [graphOptions.writeConsistency] Overrides the [consistency
* level]{@link module:types~consistencies} defined in the query options for graph write queries.
*/
/**
* Creates a new instance of {@link Client}.
* @classdesc
* Represents a database client that maintains multiple connections to the cluster nodes, providing methods to
* execute CQL statements.
*
* The Client
uses [policies]{@link module:policies} to decide which nodes to connect to, which node
* to use per each query execution, when it should retry failed or timed-out executions and how reconnection to down
* nodes should be made.
*
By default, a [DefaultMetrics]{@link module:metrics~DefaultMetrics} instance is used.
* @type {ClientMetrics} */ this.metrics = this.options.metrics; this._graphExecutor = new GraphExecutor(this, options, this._execute); } util.inherits(Client, events.EventEmitter); /** * Emitted when a new host is added to the cluster. *When the {@link Client} is already connected, it resolves immediately.
*It returns a Promise
when a callback
is not provided.
The query can be prepared (recommended) or not depending on the [prepare]{@linkcode QueryOptions} flag.
** Some execution failures can be handled transparently by the driver, according to the * [RetryPolicy]{@linkcode module:policies/retry~RetryPolicy} or the * [SpeculativeExecutionPolicy]{@linkcode module:policies/speculativeExecution} used. *
*It returns a Promise
when a callback
is not provided.
It returns a Promise
when a callback
is not provided.
err
and result
. When not defined, the method will return a promise.
* @example rowCallback
for each row as soon as they are received. Calls the final
* callback
after all rows have been sent, or when there is an error.
* * The query can be prepared (recommended) or not depending on the [prepare]{@linkcode QueryOptions} flag. *
* @param {String} query The query to execute * @param {Array|Object} [params] Array of parameter values or an associative array (object) containing parameter names * as keys and its value. * @param {QueryOptions} [options] The query options. * @param {function} rowCallback ExecutesrowCallback(n, row)
per each row received, where n is the row
* index and row is the current Row.
* @param {function} [callback] Executes callback(err, result)
after all rows have been received.
*
* When dealing with paged results, [ResultSet#nextPage()]{@link module:types~ResultSet#nextPage} method can be used
* to retrieve the following page. In that case, rowCallback()
will be again called for each row and
* the final callback will be invoked when all rows in the following page has been retrieved.
*
* The stream is a [ReadableStream]{@linkcode https://nodejs.org/api/stream.html#stream_class_stream_readable} object * that emits rows. * It can be piped downstream and provides automatic pause/resume logic (it buffers when not read). *
** The query can be prepared (recommended) or not depending on {@link QueryOptions}.prepare flag. Retries on multiple * hosts if needed. *
* @param {String} query The query to prepare and execute. * @param {Array|Object} [params] Array of parameter values or an associative array (object) containing parameter names * as keys and its value * @param {QueryOptions} [options] The query options. * @param {function} [callback] executes callback(err) after all rows have been received or if there is an error * @returns {ResultStream} */ Client.prototype.stream = function (query, params, options, callback) { callback = callback || utils.noop; // NOTE: the nodejs stream maintains yet another internal buffer // we rely on the default stream implementation to keep memory // usage reasonable. const resultStream = new types.ResultStream({ objectMode: 1 }); function onFinish(err, result) { if (err) { resultStream.emit('error', err); } if (result && result.nextPage ) { // allows for throttling as per the // default nodejs stream implementation resultStream._valve(function pageValve() { try { result.nextPage(); } catch( ex ) { resultStream.emit('error', ex ); } }); return; } // Explicitly dropping the valve (closure) resultStream._valve(null); resultStream.add(null); callback(err); } let sync = true; this.eachRow(query, params, options, function rowCallback(n, row) { resultStream.add(row); }, function eachRowFinished(err, result) { if (sync) { // Prevent sync callback return setImmediate(function eachRowFinishedImmediate() { onFinish(err, result); }); } onFinish(err, result); }); sync = false; return resultStream; }; /** * Executes batch of queries on an available connection to a host. *It returns a Promise
when a callback
is not provided.
* The information provided in the returned object only represents the state at the moment this method was called and * it's not maintained in sync with the driver metadata. *
* @returns {ClientState} A [ClientState]{@linkcode module:metadata~ClientState} instance. */ Client.prototype.getState = function () { return ClientState.from(this); }; Client.prototype.log = utils.log; /** * Closes all connections to all hosts. *It returns a Promise
when a callback
is not provided.