Node JS version
This commit is contained in:
139
node_modules/cassandra-driver/lib/policies/address-resolution.js
generated
vendored
Normal file
139
node_modules/cassandra-driver/lib/policies/address-resolution.js
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 dns = require('dns');
|
||||
const util = require('util');
|
||||
const utils = require('../utils');
|
||||
/** @module policies/addressResolution */
|
||||
/**
|
||||
* @class
|
||||
* @classdesc
|
||||
* Translates IP addresses received from Cassandra nodes into locally queryable
|
||||
* addresses.
|
||||
* <p>
|
||||
* The driver auto-detects new Cassandra nodes added to the cluster through server
|
||||
* side pushed notifications and through checking the system tables. For each
|
||||
* node, the address received will correspond to the address set as
|
||||
* <code>rpc_address</code> in the node yaml file. In most case, this is the correct
|
||||
* address to use by the driver and that is what is used by default. However,
|
||||
* sometimes the addresses received through this mechanism will either not be
|
||||
* reachable directly by the driver or should not be the preferred address to use
|
||||
* to reach the node (for instance, the <code>rpc_address</code> set on Cassandra nodes
|
||||
* might be a private IP, but some clients may have to use a public IP, or
|
||||
* pass by a router to reach that node). This interface allows to deal with
|
||||
* such cases, by allowing to translate an address as sent by a Cassandra node
|
||||
* to another address to be used by the driver for connection.
|
||||
* <p>
|
||||
* Please note that the contact points addresses provided while creating the
|
||||
* {@link Client} instance are not "translated", only IP address retrieve from or sent
|
||||
* by Cassandra nodes to the driver are.
|
||||
* @constructor
|
||||
*/
|
||||
function AddressTranslator() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a Cassandra <code>rpc_address</code> to another address if necessary.
|
||||
* @param {String} address the address of a node as returned by Cassandra.
|
||||
* <p>
|
||||
* Note that if the <code>rpc_address</code> of a node has been configured to <code>0.0.0.0</code>
|
||||
* server side, then the provided address will be the node <code>listen_address</code>,
|
||||
* *not* <code>0.0.0.0</code>.
|
||||
* </p>
|
||||
* @param {Number} port The port number, as specified in the [protocolOptions]{@link ClientOptions} at Client instance creation (9042 by default).
|
||||
* @param {Function} callback Callback to invoke with endpoint as first parameter.
|
||||
* The endpoint is an string composed of the IP address and the port number in the format <code>ipAddress:port</code>.
|
||||
*/
|
||||
AddressTranslator.prototype.translate = function (address, port, callback) {
|
||||
callback(address + ':' + port);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @classdesc
|
||||
* {@link AddressTranslator} implementation for multi-region EC2 deployments <strong>where clients are also deployed in EC2</strong>.
|
||||
* <p>
|
||||
* Its distinctive feature is that it translates addresses according to the location of the Cassandra host:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>addresses in different EC2 regions (than the client) are unchanged</li>
|
||||
* <li>addresses in the same EC2 region are <strong>translated to private IPs</strong></li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This optimizes network costs, because Amazon charges more for communication over public IPs.
|
||||
* </p>
|
||||
* @constructor
|
||||
*/
|
||||
function EC2MultiRegionTranslator() {
|
||||
|
||||
}
|
||||
|
||||
util.inherits(EC2MultiRegionTranslator, AddressTranslator);
|
||||
|
||||
/**
|
||||
* Addresses in the same EC2 region are translated to private IPs and addresses in
|
||||
* different EC2 regions (than the client) are unchanged
|
||||
*/
|
||||
EC2MultiRegionTranslator.prototype.translate = function (address, port, callback) {
|
||||
let newAddress = address;
|
||||
const self = this;
|
||||
let name;
|
||||
utils.series([
|
||||
function resolve(next) {
|
||||
dns.reverse(address, function (err, hostNames) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!hostNames) {
|
||||
return next();
|
||||
}
|
||||
name = hostNames[0];
|
||||
next();
|
||||
});
|
||||
},
|
||||
function lookup(next) {
|
||||
if (!name) {
|
||||
return next();
|
||||
}
|
||||
dns.lookup(name, function (err, lookupAddress) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
newAddress = lookupAddress;
|
||||
next();
|
||||
});
|
||||
}], function (err) {
|
||||
if (err) {
|
||||
//there was an issue while doing dns resolution
|
||||
self.logError(address, err);
|
||||
}
|
||||
callback(newAddress + ':' + port);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Log method called to log errors that occurred while performing dns resolution.
|
||||
* You can assign your own method to the class instance to do proper logging.
|
||||
* @param {String} address
|
||||
* @param {Error} err
|
||||
*/
|
||||
EC2MultiRegionTranslator.prototype.logError = function (address, err) {
|
||||
//Do nothing by default
|
||||
};
|
||||
|
||||
exports.AddressTranslator = AddressTranslator;
|
||||
exports.EC2MultiRegionTranslator = EC2MultiRegionTranslator;
|
210
node_modules/cassandra-driver/lib/policies/index.d.ts
generated
vendored
Normal file
210
node_modules/cassandra-driver/lib/policies/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Client, EmptyCallback, ExecutionOptions, Host, HostMap } from '../../';
|
||||
import { types } from '../types';
|
||||
|
||||
|
||||
export namespace policies {
|
||||
function defaultAddressTranslator(): addressResolution.AddressTranslator;
|
||||
|
||||
function defaultLoadBalancingPolicy(localDc?: string): loadBalancing.LoadBalancingPolicy;
|
||||
|
||||
function defaultReconnectionPolicy(): reconnection.ReconnectionPolicy;
|
||||
|
||||
function defaultRetryPolicy(): retry.RetryPolicy;
|
||||
|
||||
function defaultSpeculativeExecutionPolicy(): speculativeExecution.SpeculativeExecutionPolicy;
|
||||
|
||||
function defaultTimestampGenerator(): timestampGeneration.TimestampGenerator;
|
||||
|
||||
namespace addressResolution {
|
||||
interface AddressTranslator {
|
||||
translate(address: string, port: number, callback: Function): void;
|
||||
}
|
||||
|
||||
class EC2MultiRegionTranslator implements AddressTranslator {
|
||||
translate(address: string, port: number, callback: Function): void;
|
||||
}
|
||||
}
|
||||
|
||||
namespace loadBalancing {
|
||||
abstract class LoadBalancingPolicy {
|
||||
init(client: Client, hosts: HostMap, callback: EmptyCallback): void;
|
||||
|
||||
getDistance(host: Host): types.distance;
|
||||
|
||||
newQueryPlan(
|
||||
keyspace: string,
|
||||
executionOptions: ExecutionOptions,
|
||||
callback: (error: Error, iterator: Iterator<Host>) => void): void;
|
||||
|
||||
getOptions(): Map<string, object>;
|
||||
}
|
||||
|
||||
class DCAwareRoundRobinPolicy extends LoadBalancingPolicy {
|
||||
constructor(localDc: string);
|
||||
}
|
||||
|
||||
class TokenAwarePolicy extends LoadBalancingPolicy {
|
||||
constructor(childPolicy: LoadBalancingPolicy);
|
||||
}
|
||||
|
||||
class AllowListPolicy extends LoadBalancingPolicy {
|
||||
constructor(childPolicy: LoadBalancingPolicy, allowList: string[]);
|
||||
}
|
||||
|
||||
class WhiteListPolicy extends AllowListPolicy {
|
||||
}
|
||||
|
||||
class RoundRobinPolicy extends LoadBalancingPolicy {
|
||||
constructor();
|
||||
}
|
||||
|
||||
class DefaultLoadBalancingPolicy extends LoadBalancingPolicy {
|
||||
constructor(options?: { localDc?: string, filter?: (host: Host) => boolean });
|
||||
}
|
||||
}
|
||||
|
||||
namespace reconnection {
|
||||
class ConstantReconnectionPolicy implements ReconnectionPolicy {
|
||||
constructor(delay: number);
|
||||
|
||||
getOptions(): Map<string, object>;
|
||||
|
||||
newSchedule(): Iterator<number>;
|
||||
|
||||
}
|
||||
|
||||
class ExponentialReconnectionPolicy implements ReconnectionPolicy {
|
||||
constructor(baseDelay: number, maxDelay: number, startWithNoDelay?: boolean);
|
||||
|
||||
getOptions(): Map<string, object>;
|
||||
|
||||
newSchedule(): Iterator<number>;
|
||||
}
|
||||
|
||||
interface ReconnectionPolicy {
|
||||
getOptions(): Map<string, object>;
|
||||
|
||||
newSchedule(): Iterator<number>;
|
||||
}
|
||||
}
|
||||
|
||||
namespace retry {
|
||||
class DecisionInfo {
|
||||
decision: number;
|
||||
consistency: types.consistencies;
|
||||
}
|
||||
|
||||
class OperationInfo {
|
||||
query: string;
|
||||
executionOptions: ExecutionOptions;
|
||||
nbRetry: number;
|
||||
}
|
||||
|
||||
class IdempotenceAwareRetryPolicy extends RetryPolicy {
|
||||
constructor(childPolicy: RetryPolicy);
|
||||
}
|
||||
|
||||
class FallthroughRetryPolicy extends RetryPolicy {
|
||||
constructor();
|
||||
}
|
||||
|
||||
class RetryPolicy {
|
||||
onReadTimeout(
|
||||
info: OperationInfo,
|
||||
consistency: types.consistencies,
|
||||
received: number,
|
||||
blockFor: number,
|
||||
isDataPresent: boolean): DecisionInfo;
|
||||
|
||||
onRequestError(info: OperationInfo, consistency: types.consistencies, err: Error): DecisionInfo;
|
||||
|
||||
onUnavailable(
|
||||
info: OperationInfo, consistency: types.consistencies, required: number, alive: boolean): DecisionInfo;
|
||||
|
||||
onWriteTimeout(
|
||||
info: OperationInfo,
|
||||
consistency: types.consistencies,
|
||||
received: number,
|
||||
blockFor: number,
|
||||
writeType: string): DecisionInfo;
|
||||
|
||||
rethrowResult(): DecisionInfo;
|
||||
|
||||
retryResult(consistency: types.consistencies, useCurrentHost?: boolean): DecisionInfo;
|
||||
}
|
||||
|
||||
namespace RetryDecision {
|
||||
enum retryDecision {
|
||||
ignore,
|
||||
rethrow,
|
||||
retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace speculativeExecution {
|
||||
class ConstantSpeculativeExecutionPolicy implements SpeculativeExecutionPolicy {
|
||||
constructor(delay: number, maxSpeculativeExecutions: number);
|
||||
|
||||
getOptions(): Map<string, object>;
|
||||
|
||||
init(client: Client): void;
|
||||
|
||||
newPlan(keyspace: string, queryInfo: string | Array<object>): { nextExecution: Function };
|
||||
|
||||
shutdown(): void;
|
||||
}
|
||||
|
||||
class NoSpeculativeExecutionPolicy implements SpeculativeExecutionPolicy {
|
||||
constructor();
|
||||
|
||||
getOptions(): Map<string, object>;
|
||||
|
||||
init(client: Client): void;
|
||||
|
||||
newPlan(keyspace: string, queryInfo: string | Array<object>): { nextExecution: Function };
|
||||
|
||||
shutdown(): void;
|
||||
}
|
||||
|
||||
interface SpeculativeExecutionPolicy {
|
||||
getOptions(): Map<string, object>;
|
||||
|
||||
init(client: Client): void;
|
||||
|
||||
newPlan(keyspace: string, queryInfo: string|Array<object>): { nextExecution: Function };
|
||||
|
||||
shutdown(): void;
|
||||
}
|
||||
}
|
||||
|
||||
namespace timestampGeneration {
|
||||
class MonotonicTimestampGenerator implements TimestampGenerator {
|
||||
constructor(warningThreshold: number, minLogInterval: number);
|
||||
|
||||
getDate(): number;
|
||||
|
||||
next(client: Client): types.Long | number;
|
||||
}
|
||||
|
||||
interface TimestampGenerator {
|
||||
next(client: Client): types.Long|number;
|
||||
}
|
||||
}
|
||||
}
|
84
node_modules/cassandra-driver/lib/policies/index.js
generated
vendored
Normal file
84
node_modules/cassandra-driver/lib/policies/index.js
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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';
|
||||
|
||||
/**
|
||||
* Contains driver tuning policies to determine [load balancing]{@link module:policies/loadBalancing},
|
||||
* [retrying]{@link module:policies/retry} queries, [reconnecting]{@link module:policies/reconnection} to a node,
|
||||
* [address resolution]{@link module:policies/addressResolution},
|
||||
* [timestamp generation]{@link module:policies/timestampGeneration} and
|
||||
* [speculative execution]{@link module:policies/speculativeExecution}.
|
||||
* @module policies
|
||||
*/
|
||||
const addressResolution = exports.addressResolution = require('./address-resolution');
|
||||
const loadBalancing = exports.loadBalancing = require('./load-balancing');
|
||||
const reconnection = exports.reconnection = require('./reconnection');
|
||||
const retry = exports.retry = require('./retry');
|
||||
const speculativeExecution = exports.speculativeExecution = require('./speculative-execution');
|
||||
const timestampGeneration = exports.timestampGeneration = require('./timestamp-generation');
|
||||
|
||||
/**
|
||||
* Returns a new instance of the default address translator policy used by the driver.
|
||||
* @returns {AddressTranslator}
|
||||
*/
|
||||
exports.defaultAddressTranslator = function () {
|
||||
return new addressResolution.AddressTranslator();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a new instance of the default load-balancing policy used by the driver.
|
||||
* @param {string} [localDc] When provided, it sets the data center that is going to be used as local for the
|
||||
* load-balancing policy instance.
|
||||
* <p>When localDc is undefined, the load-balancing policy instance will use the <code>localDataCenter</code>
|
||||
* provided in the {@link ClientOptions}.</p>
|
||||
* @returns {LoadBalancingPolicy}
|
||||
*/
|
||||
exports.defaultLoadBalancingPolicy = function (localDc) {
|
||||
return new loadBalancing.DefaultLoadBalancingPolicy(localDc);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a new instance of the default retry policy used by the driver.
|
||||
* @returns {RetryPolicy}
|
||||
*/
|
||||
exports.defaultRetryPolicy = function () {
|
||||
return new retry.RetryPolicy();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a new instance of the default reconnection policy used by the driver.
|
||||
* @returns {ReconnectionPolicy}
|
||||
*/
|
||||
exports.defaultReconnectionPolicy = function () {
|
||||
return new reconnection.ExponentialReconnectionPolicy(1000, 10 * 60 * 1000, false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new instance of the default speculative execution policy used by the driver.
|
||||
* @returns {SpeculativeExecutionPolicy}
|
||||
*/
|
||||
exports.defaultSpeculativeExecutionPolicy = function () {
|
||||
return new speculativeExecution.NoSpeculativeExecutionPolicy();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a new instance of the default timestamp generator used by the driver.
|
||||
* @returns {TimestampGenerator}
|
||||
*/
|
||||
exports.defaultTimestampGenerator = function () {
|
||||
return new timestampGeneration.MonotonicTimestampGenerator();
|
||||
};
|
883
node_modules/cassandra-driver/lib/policies/load-balancing.js
generated
vendored
Normal file
883
node_modules/cassandra-driver/lib/policies/load-balancing.js
generated
vendored
Normal file
@@ -0,0 +1,883 @@
|
||||
/*
|
||||
* 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 util = require('util');
|
||||
const types = require('../types');
|
||||
const utils = require('../utils.js');
|
||||
const errors = require('../errors.js');
|
||||
|
||||
const doneIteratorObject = Object.freeze({ done: true });
|
||||
const newlyUpInterval = 60000;
|
||||
|
||||
/** @module policies/loadBalancing */
|
||||
/**
|
||||
* Base class for Load Balancing Policies
|
||||
* @constructor
|
||||
*/
|
||||
function LoadBalancingPolicy() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the load balancing policy, called after the driver obtained the information of the cluster.
|
||||
* @param {Client} client
|
||||
* @param {HostMap} hosts
|
||||
* @param {Function} callback
|
||||
*/
|
||||
LoadBalancingPolicy.prototype.init = function (client, hosts, callback) {
|
||||
this.client = client;
|
||||
this.hosts = hosts;
|
||||
callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the distance assigned by this policy to the provided host.
|
||||
* @param {Host} host
|
||||
*/
|
||||
LoadBalancingPolicy.prototype.getDistance = function (host) {
|
||||
return types.distance.local;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an iterator with the hosts for a new query.
|
||||
* Each new query will call this method. The first host in the result will
|
||||
* then be used to perform the query.
|
||||
* @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
|
||||
* @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
|
||||
* @param {Function} callback The function to be invoked with the error as first parameter and the host iterator as
|
||||
* second parameter.
|
||||
*/
|
||||
LoadBalancingPolicy.prototype.newQueryPlan = function (keyspace, executionOptions, callback) {
|
||||
callback(new Error('You must implement a query plan for the LoadBalancingPolicy class'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
LoadBalancingPolicy.prototype.getOptions = function () {
|
||||
return new Map();
|
||||
};
|
||||
|
||||
/**
|
||||
* This policy yield nodes in a round-robin fashion.
|
||||
* @extends LoadBalancingPolicy
|
||||
* @constructor
|
||||
*/
|
||||
function RoundRobinPolicy() {
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
util.inherits(RoundRobinPolicy, LoadBalancingPolicy);
|
||||
|
||||
/**
|
||||
* Returns an iterator with the hosts to be used as coordinator for a query.
|
||||
* @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
|
||||
* @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
|
||||
* @param {Function} callback The function to be invoked with the error as first parameter and the host iterator as
|
||||
* second parameter.
|
||||
*/
|
||||
RoundRobinPolicy.prototype.newQueryPlan = function (keyspace, executionOptions, callback) {
|
||||
if (!this.hosts) {
|
||||
return callback(new Error('Load balancing policy not initialized'));
|
||||
}
|
||||
const hosts = this.hosts.values();
|
||||
const self = this;
|
||||
let counter = 0;
|
||||
|
||||
let planIndex = self.index % hosts.length;
|
||||
self.index += 1;
|
||||
if (self.index >= utils.maxInt) {
|
||||
self.index = 0;
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
next: function () {
|
||||
if (++counter > hosts.length) {
|
||||
return doneIteratorObject;
|
||||
}
|
||||
return {value: hosts[planIndex++ % hosts.length], done: false};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* A data-center aware Round-robin load balancing policy.
|
||||
* This policy provides round-robin queries over the nodes of the local
|
||||
* data center.
|
||||
* @param {?String} [localDc] local datacenter name. This value overrides the 'localDataCenter' Client option \
|
||||
* and is useful for cases where you have multiple execution profiles that you intend on using for routing
|
||||
* requests to different data centers.
|
||||
* @extends {LoadBalancingPolicy}
|
||||
* @constructor
|
||||
*/
|
||||
function DCAwareRoundRobinPolicy(localDc) {
|
||||
this.localDc = localDc;
|
||||
this.index = 0;
|
||||
/** @type {Array} */
|
||||
this.localHostsArray = null;
|
||||
}
|
||||
|
||||
util.inherits(DCAwareRoundRobinPolicy, LoadBalancingPolicy);
|
||||
|
||||
/**
|
||||
* Initializes the load balancing policy.
|
||||
* @param {Client} client
|
||||
* @param {HostMap} hosts
|
||||
* @param {Function} callback
|
||||
*/
|
||||
DCAwareRoundRobinPolicy.prototype.init = function (client, hosts, callback) {
|
||||
this.client = client;
|
||||
this.hosts = hosts;
|
||||
hosts.on('add', this._cleanHostCache.bind(this));
|
||||
hosts.on('remove', this._cleanHostCache.bind(this));
|
||||
|
||||
try {
|
||||
setLocalDc(this, client, this.hosts);
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the distance depending on the datacenter.
|
||||
* @param {Host} host
|
||||
*/
|
||||
DCAwareRoundRobinPolicy.prototype.getDistance = function (host) {
|
||||
if (host.datacenter === this.localDc) {
|
||||
return types.distance.local;
|
||||
}
|
||||
|
||||
return types.distance.ignored;
|
||||
};
|
||||
|
||||
DCAwareRoundRobinPolicy.prototype._cleanHostCache = function () {
|
||||
this.localHostsArray = null;
|
||||
};
|
||||
|
||||
DCAwareRoundRobinPolicy.prototype._resolveLocalHosts = function() {
|
||||
const hosts = this.hosts.values();
|
||||
if (this.localHostsArray) {
|
||||
//there were already calculated
|
||||
return;
|
||||
}
|
||||
this.localHostsArray = [];
|
||||
hosts.forEach(function (h) {
|
||||
if (!h.datacenter) {
|
||||
//not a remote dc node
|
||||
return;
|
||||
}
|
||||
if (h.datacenter === this.localDc) {
|
||||
this.localHostsArray.push(h);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* It returns an iterator that yields local nodes.
|
||||
* @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
|
||||
* @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
|
||||
* @param {Function} callback The function to be invoked with the error as first parameter and the host iterator as
|
||||
* second parameter.
|
||||
*/
|
||||
DCAwareRoundRobinPolicy.prototype.newQueryPlan = function (keyspace, executionOptions, callback) {
|
||||
if (!this.hosts) {
|
||||
return callback(new Error('Load balancing policy not initialized'));
|
||||
}
|
||||
this.index += 1;
|
||||
if (this.index >= utils.maxInt) {
|
||||
this.index = 0;
|
||||
}
|
||||
this._resolveLocalHosts();
|
||||
// Use a local reference of hosts
|
||||
const localHostsArray = this.localHostsArray;
|
||||
let planLocalIndex = this.index;
|
||||
let counter = 0;
|
||||
callback(null, {
|
||||
next: function () {
|
||||
let host;
|
||||
if (counter++ < localHostsArray.length) {
|
||||
host = localHostsArray[planLocalIndex++ % localHostsArray.length];
|
||||
return { value: host, done: false };
|
||||
}
|
||||
return doneIteratorObject;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
DCAwareRoundRobinPolicy.prototype.getOptions = function () {
|
||||
return new Map([
|
||||
['localDataCenter', this.localDc ]
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* A wrapper load balancing policy that add token awareness to a child policy.
|
||||
* @param {LoadBalancingPolicy} childPolicy
|
||||
* @extends LoadBalancingPolicy
|
||||
* @constructor
|
||||
*/
|
||||
function TokenAwarePolicy (childPolicy) {
|
||||
if (!childPolicy) {
|
||||
throw new Error("You must specify a child load balancing policy");
|
||||
}
|
||||
this.childPolicy = childPolicy;
|
||||
}
|
||||
|
||||
util.inherits(TokenAwarePolicy, LoadBalancingPolicy);
|
||||
|
||||
TokenAwarePolicy.prototype.init = function (client, hosts, callback) {
|
||||
this.client = client;
|
||||
this.hosts = hosts;
|
||||
this.childPolicy.init(client, hosts, callback);
|
||||
};
|
||||
|
||||
TokenAwarePolicy.prototype.getDistance = function (host) {
|
||||
return this.childPolicy.getDistance(host);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the hosts to use for a new query.
|
||||
* The returned plan will return local replicas first, if replicas can be determined, followed by the plan of the
|
||||
* child policy.
|
||||
* @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
|
||||
* @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
|
||||
* @param {Function} callback The function to be invoked with the error as first parameter and the host iterator as
|
||||
* second parameter.
|
||||
*/
|
||||
TokenAwarePolicy.prototype.newQueryPlan = function (keyspace, executionOptions, callback) {
|
||||
let routingKey;
|
||||
if (executionOptions) {
|
||||
routingKey = executionOptions.getRoutingKey();
|
||||
if (executionOptions.getKeyspace()) {
|
||||
keyspace = executionOptions.getKeyspace();
|
||||
}
|
||||
}
|
||||
let replicas;
|
||||
if (routingKey) {
|
||||
replicas = this.client.getReplicas(keyspace, routingKey);
|
||||
}
|
||||
if (!routingKey || !replicas) {
|
||||
return this.childPolicy.newQueryPlan(keyspace, executionOptions, callback);
|
||||
}
|
||||
const iterator = new TokenAwareIterator(keyspace, executionOptions, replicas, this.childPolicy);
|
||||
iterator.iterate(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* An iterator that holds the context for the subsequent next() calls
|
||||
* @param {String} keyspace
|
||||
* @param {ExecutionOptions} execOptions
|
||||
* @param {Array} replicas
|
||||
* @param childPolicy
|
||||
* @constructor
|
||||
* @ignore
|
||||
*/
|
||||
function TokenAwareIterator(keyspace, execOptions, replicas, childPolicy) {
|
||||
this.keyspace = keyspace;
|
||||
this.childPolicy = childPolicy;
|
||||
this.options = execOptions;
|
||||
this.localReplicas = [];
|
||||
this.replicaIndex = 0;
|
||||
this.replicaMap = {};
|
||||
this.childIterator = null;
|
||||
// Memoize the local replicas
|
||||
// The amount of local replicas should be defined before start iterating, in order to select an
|
||||
// appropriate (pseudo random) startIndex
|
||||
for (let i = 0; i < replicas.length; i++) {
|
||||
const host = replicas[i];
|
||||
if (this.childPolicy.getDistance(host) !== types.distance.local) {
|
||||
continue;
|
||||
}
|
||||
this.replicaMap[host.address] = true;
|
||||
this.localReplicas.push(host);
|
||||
}
|
||||
// We use a PRNG to set the replica index
|
||||
// We only care about proportional fair scheduling between replicas of a given token
|
||||
// Math.random() has an extremely short permutation cycle length but we don't care about collisions
|
||||
this.startIndex = Math.floor(Math.random() * this.localReplicas.length);
|
||||
}
|
||||
|
||||
TokenAwareIterator.prototype.iterate = function (callback) {
|
||||
//Load the child policy hosts
|
||||
const self = this;
|
||||
this.childPolicy.newQueryPlan(this.keyspace, this.options, function (err, iterator) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
//get the iterator of the child policy in case is needed
|
||||
self.childIterator = iterator;
|
||||
callback(null, {
|
||||
next: function () { return self.computeNext(); }
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
TokenAwareIterator.prototype.computeNext = function () {
|
||||
let host;
|
||||
if (this.replicaIndex < this.localReplicas.length) {
|
||||
host = this.localReplicas[(this.startIndex + (this.replicaIndex++)) % this.localReplicas.length];
|
||||
return { value: host, done: false };
|
||||
}
|
||||
// Return hosts from child policy
|
||||
let item;
|
||||
while ((item = this.childIterator.next()) && !item.done) {
|
||||
if (this.replicaMap[item.value.address]) {
|
||||
// Avoid yielding local replicas from the child load balancing policy query plan
|
||||
continue;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
return doneIteratorObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
TokenAwarePolicy.prototype.getOptions = function () {
|
||||
const map = new Map([
|
||||
['childPolicy', this.childPolicy.constructor !== undefined ? this.childPolicy.constructor.name : null ]
|
||||
]);
|
||||
|
||||
if (this.childPolicy instanceof DCAwareRoundRobinPolicy) {
|
||||
map.set('localDataCenter', this.childPolicy.localDc);
|
||||
}
|
||||
|
||||
return map;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new policy that wraps the provided child policy but only "allow" hosts
|
||||
* from the provided list.
|
||||
* @class
|
||||
* @classdesc
|
||||
* A load balancing policy wrapper that ensure that only hosts from a provided
|
||||
* allow list will ever be returned.
|
||||
* <p>
|
||||
* This policy wraps another load balancing policy and will delegate the choice
|
||||
* of hosts to the wrapped policy with the exception that only hosts contained
|
||||
* in the allow list provided when constructing this policy will ever be
|
||||
* returned. Any host not in the while list will be considered ignored
|
||||
* and thus will not be connected to.
|
||||
* <p>
|
||||
* This policy can be useful to ensure that the driver only connects to a
|
||||
* predefined set of hosts. Keep in mind however that this policy defeats
|
||||
* somewhat the host auto-detection of the driver. As such, this policy is only
|
||||
* useful in a few special cases or for testing, but is not optimal in general.
|
||||
* If all you want to do is limiting connections to hosts of the local
|
||||
* data-center then you should use DCAwareRoundRobinPolicy and *not* this policy
|
||||
* in particular.
|
||||
* @param {LoadBalancingPolicy} childPolicy the wrapped policy.
|
||||
* @param {Array.<string>} allowList The hosts address in the format ipAddress:port.
|
||||
* Only hosts from this list may get connected
|
||||
* to (whether they will get connected to or not depends on the child policy).
|
||||
* @extends LoadBalancingPolicy
|
||||
* @constructor
|
||||
*/
|
||||
function AllowListPolicy (childPolicy, allowList) {
|
||||
if (!childPolicy) {
|
||||
throw new Error("You must specify a child load balancing policy");
|
||||
}
|
||||
if (!Array.isArray(allowList)) {
|
||||
throw new Error("You must provide the list of allowed host addresses");
|
||||
}
|
||||
|
||||
this.childPolicy = childPolicy;
|
||||
this.allowList = new Map(allowList.map(address => [ address, true ]));
|
||||
}
|
||||
|
||||
util.inherits(AllowListPolicy, LoadBalancingPolicy);
|
||||
|
||||
AllowListPolicy.prototype.init = function (client, hosts, callback) {
|
||||
this.childPolicy.init(client, hosts, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Uses the child policy to return the distance to the host if included in the allow list.
|
||||
* Any host not in the while list will be considered ignored.
|
||||
* @param host
|
||||
*/
|
||||
AllowListPolicy.prototype.getDistance = function (host) {
|
||||
if (!this._contains(host)) {
|
||||
return types.distance.ignored;
|
||||
}
|
||||
return this.childPolicy.getDistance(host);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Host} host
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
AllowListPolicy.prototype._contains = function (host) {
|
||||
return !!this.allowList.get(host.address);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the hosts to use for a new query filtered by the allow list.
|
||||
*/
|
||||
AllowListPolicy.prototype.newQueryPlan = function (keyspace, info, callback) {
|
||||
const self = this;
|
||||
this.childPolicy.newQueryPlan(keyspace, info, function (err, iterator) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, self._filter(iterator));
|
||||
});
|
||||
};
|
||||
|
||||
AllowListPolicy.prototype._filter = function (childIterator) {
|
||||
const self = this;
|
||||
return {
|
||||
next: function () {
|
||||
const item = childIterator.next();
|
||||
if (!item.done && !self._contains(item.value)) {
|
||||
return this.next();
|
||||
}
|
||||
return item;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
AllowListPolicy.prototype.getOptions = function () {
|
||||
return new Map([
|
||||
['childPolicy', this.childPolicy.constructor !== undefined ? this.childPolicy.constructor.name : null ],
|
||||
['allowList', Array.from(this.allowList.keys())]
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new instance of the policy.
|
||||
* @classdesc
|
||||
* Exposed for backward-compatibility only, it's recommended that you use {@link AllowListPolicy} instead.
|
||||
* @param {LoadBalancingPolicy} childPolicy the wrapped policy.
|
||||
* @param {Array.<string>} allowList The hosts address in the format ipAddress:port.
|
||||
* Only hosts from this list may get connected to (whether they will get connected to or not depends on the child
|
||||
* policy).
|
||||
* @extends AllowListPolicy
|
||||
* @deprecated Use allow-list instead. It will be removed in future major versions.
|
||||
* @constructor
|
||||
*/
|
||||
function WhiteListPolicy(childPolicy, allowList) {
|
||||
AllowListPolicy.call(this, childPolicy, allowList);
|
||||
}
|
||||
|
||||
util.inherits(WhiteListPolicy, AllowListPolicy);
|
||||
|
||||
/**
|
||||
* A load-balancing policy implementation that attempts to fairly distribute the load based on the amount of in-flight
|
||||
* request per hosts. The local replicas are initially shuffled and
|
||||
* <a href="https://www.eecs.harvard.edu/~michaelm/postscripts/mythesis.pdf">between the first two nodes in the
|
||||
* shuffled list, the one with fewer in-flight requests is selected as coordinator</a>.
|
||||
*
|
||||
* <p>
|
||||
* Additionally, it detects unresponsive replicas and reorders them at the back of the query plan.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* For graph analytics queries, it uses the preferred analytics graph server previously obtained by driver as first
|
||||
* host in the query plan.
|
||||
* </p>
|
||||
*/
|
||||
class DefaultLoadBalancingPolicy extends LoadBalancingPolicy {
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>DefaultLoadBalancingPolicy</code>.
|
||||
* @param {String|Object} [options] The local data center name or the optional policy options object.
|
||||
* <p>
|
||||
* Note that when providing the local data center name, it overrides <code>localDataCenter</code> option at
|
||||
* <code>Client</code> level.
|
||||
* </p>
|
||||
* @param {String} [options.localDc] local data center name. This value overrides the 'localDataCenter' Client option
|
||||
* and is useful for cases where you have multiple execution profiles that you intend on using for routing
|
||||
* requests to different data centers.
|
||||
* @param {Function} [options.filter] A function to apply to determine if hosts are included in the query plan.
|
||||
* The function takes a Host parameter and returns a Boolean.
|
||||
*/
|
||||
constructor(options) {
|
||||
super();
|
||||
|
||||
if (typeof options === 'string') {
|
||||
options = { localDc: options };
|
||||
} else if (!options) {
|
||||
options = utils.emptyObject;
|
||||
}
|
||||
|
||||
this._client = null;
|
||||
this._hosts = null;
|
||||
this._filteredHosts = null;
|
||||
this._preferredHost = null;
|
||||
this._index = 0;
|
||||
this.localDc = options.localDc;
|
||||
this._filter = options.filter || this._defaultFilter;
|
||||
|
||||
// Allow some checks to be injected
|
||||
if (options.isHostNewlyUp) {
|
||||
this._isHostNewlyUp = options.isHostNewlyUp;
|
||||
}
|
||||
if (options.healthCheck) {
|
||||
this._healthCheck = options.healthCheck;
|
||||
}
|
||||
if (options.compare) {
|
||||
this._compare = options.compare;
|
||||
}
|
||||
if (options.getReplicas) {
|
||||
this._getReplicas = options.getReplicas;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the load balancing policy, called after the driver obtained the information of the cluster.
|
||||
* @param {Client} client
|
||||
* @param {HostMap} hosts
|
||||
* @param {Function} callback
|
||||
*/
|
||||
init(client, hosts, callback) {
|
||||
this._client = client;
|
||||
this._hosts = hosts;
|
||||
|
||||
// Clean local host cache
|
||||
this._hosts.on('add', () => this._filteredHosts = null);
|
||||
this._hosts.on('remove', () => this._filteredHosts = null);
|
||||
|
||||
try {
|
||||
setLocalDc(this, client, this._hosts);
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance assigned by this policy to the provided host, relatively to the client instance.
|
||||
* @param {Host} host
|
||||
*/
|
||||
getDistance(host) {
|
||||
if (this._preferredHost !== null && host === this._preferredHost) {
|
||||
// Set the last preferred host as local.
|
||||
// It ensures that the pool for the graph analytics host has the appropriate size
|
||||
return types.distance.local;
|
||||
}
|
||||
|
||||
if (!this._filter(host)) {
|
||||
return types.distance.ignored;
|
||||
}
|
||||
|
||||
return host.datacenter === this.localDc ? types.distance.local : types.distance.ignored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a host iterator to be used for a query execution.
|
||||
* @override
|
||||
* @param {String} keyspace
|
||||
* @param {ExecutionOptions} executionOptions
|
||||
* @param {Function} callback
|
||||
*/
|
||||
newQueryPlan(keyspace, executionOptions, callback) {
|
||||
let routingKey;
|
||||
let preferredHost;
|
||||
|
||||
if (executionOptions) {
|
||||
routingKey = executionOptions.getRoutingKey();
|
||||
|
||||
if (executionOptions.getKeyspace()) {
|
||||
keyspace = executionOptions.getKeyspace();
|
||||
}
|
||||
|
||||
preferredHost = executionOptions.getPreferredHost();
|
||||
}
|
||||
|
||||
let iterable;
|
||||
|
||||
if (!keyspace || !routingKey) {
|
||||
iterable = this._getLocalHosts();
|
||||
} else {
|
||||
iterable = this._getReplicasAndLocalHosts(keyspace, routingKey);
|
||||
}
|
||||
|
||||
if (preferredHost) {
|
||||
// Set it on an instance level field to set the distance
|
||||
this._preferredHost = preferredHost;
|
||||
iterable = DefaultLoadBalancingPolicy._getPreferredHostFirst(preferredHost, iterable);
|
||||
}
|
||||
|
||||
return callback(null, iterable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Yields the preferred host first, followed by the host in the provided iterable
|
||||
* @param preferredHost
|
||||
* @param iterable
|
||||
* @private
|
||||
*/
|
||||
static *_getPreferredHostFirst(preferredHost, iterable) {
|
||||
yield preferredHost;
|
||||
|
||||
for (const host of iterable) {
|
||||
if (host !== preferredHost) {
|
||||
yield host;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Yields the local hosts without the replicas already yielded
|
||||
* @param {Array<Host>} [localReplicas] The local replicas that we should avoid to include again
|
||||
* @private
|
||||
*/
|
||||
*_getLocalHosts(localReplicas) {
|
||||
// Use a local reference
|
||||
const hosts = this._getFilteredLocalHosts();
|
||||
const initialIndex = this._getIndex();
|
||||
|
||||
// indexOf() over an Array is a O(n) operation but given that there should be 3 to 7 replicas,
|
||||
// it shouldn't be an expensive call. Additionally, this will only be executed when the local replicas
|
||||
// have been exhausted in a lazy manner.
|
||||
const canBeYield = localReplicas
|
||||
? h => localReplicas.indexOf(h) === -1
|
||||
: h => true;
|
||||
|
||||
for (let i = 0; i < hosts.length; i++) {
|
||||
const h = hosts[(i + initialIndex) % hosts.length];
|
||||
if (canBeYield(h) && h.isUp()) {
|
||||
yield h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getReplicasAndLocalHosts(keyspace, routingKey) {
|
||||
let replicas = this._getReplicas(keyspace, routingKey);
|
||||
if (replicas === null) {
|
||||
return this._getLocalHosts();
|
||||
}
|
||||
|
||||
const filteredReplicas = [];
|
||||
let newlyUpReplica = null;
|
||||
let newlyUpReplicaTimestamp = Number.MIN_SAFE_INTEGER;
|
||||
let unhealthyReplicas = 0;
|
||||
|
||||
// Filter by DC, predicate and UP replicas
|
||||
// Use the same iteration to perform other checks: whether if its newly UP or unhealthy
|
||||
// As this is part of the hot path, we use a simple loop and avoid using Array.prototype.filter() + closure
|
||||
for (let i = 0; i < replicas.length; i++) {
|
||||
const h = replicas[i];
|
||||
if (!this._filter(h) || h.datacenter !== this.localDc || !h.isUp()) {
|
||||
continue;
|
||||
}
|
||||
const isUpSince = this._isHostNewlyUp(h);
|
||||
if (isUpSince !== null && isUpSince > newlyUpReplicaTimestamp) {
|
||||
newlyUpReplica = h;
|
||||
newlyUpReplicaTimestamp = isUpSince;
|
||||
}
|
||||
if (newlyUpReplica === null && !this._healthCheck(h)) {
|
||||
unhealthyReplicas++;
|
||||
}
|
||||
filteredReplicas.push(h);
|
||||
}
|
||||
|
||||
replicas = filteredReplicas;
|
||||
|
||||
// Shuffle remaining local replicas
|
||||
utils.shuffleArray(replicas);
|
||||
|
||||
if (replicas.length < 3) {
|
||||
// Avoid reordering replicas of a set of 2 as we could be doing more harm than good
|
||||
return this.yieldReplicasFirst(replicas);
|
||||
}
|
||||
|
||||
let temp;
|
||||
|
||||
if (newlyUpReplica === null) {
|
||||
if (unhealthyReplicas > 0 && unhealthyReplicas < Math.floor(replicas.length / 2 + 1)) {
|
||||
// There is one or more unhealthy replicas and there is a majority of healthy replicas
|
||||
this._sendUnhealthyToTheBack(replicas, unhealthyReplicas);
|
||||
}
|
||||
}
|
||||
else if ((newlyUpReplica === replicas[0] || newlyUpReplica === replicas[1]) && Math.random() * 4 >= 1) {
|
||||
// There is a newly UP replica and the replica in first or second position is the most recent replica
|
||||
// marked as UP and dice roll 1d4!=1 -> Send it to the back of the Array
|
||||
const index = newlyUpReplica === replicas[0] ? 0 : 1;
|
||||
temp = replicas[replicas.length - 1];
|
||||
replicas[replicas.length - 1] = replicas[index];
|
||||
replicas[index] = temp;
|
||||
}
|
||||
|
||||
if (this._compare(replicas[1], replicas[0]) > 0) {
|
||||
// Power of two random choices
|
||||
temp = replicas[0];
|
||||
replicas[0] = replicas[1];
|
||||
replicas[1] = temp;
|
||||
}
|
||||
|
||||
return this.yieldReplicasFirst(replicas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Yields the local replicas followed by the rest of local nodes.
|
||||
* @param {Array<Host>} replicas The local replicas
|
||||
*/
|
||||
*yieldReplicasFirst(replicas) {
|
||||
for (let i = 0; i < replicas.length; i++) {
|
||||
yield replicas[i];
|
||||
}
|
||||
yield* this._getLocalHosts(replicas);
|
||||
}
|
||||
|
||||
_isHostNewlyUp(h) {
|
||||
return (h.isUpSince !== null && Date.now() - h.isUpSince < newlyUpInterval) ? h.isUpSince : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean determining whether the host health is ok or not.
|
||||
* A Host is considered unhealthy when there are enough items in the queue (10 items in-flight) but the
|
||||
* Host is not responding to those requests.
|
||||
* @param {Host} h
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
_healthCheck(h) {
|
||||
return !(h.getInFlight() >= 10 && h.getResponseCount() <= 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares to host and returns 1 if it needs to favor the first host otherwise, -1.
|
||||
* @return {number}
|
||||
* @private
|
||||
*/
|
||||
_compare(h1, h2) {
|
||||
return h1.getInFlight() < h2.getInFlight() ? 1 : -1;
|
||||
}
|
||||
|
||||
_getReplicas(keyspace, routingKey) {
|
||||
return this._client.getReplicas(keyspace, routingKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Array of hosts filtered by DC and predicate.
|
||||
* @returns {Array<Host>}
|
||||
* @private
|
||||
*/
|
||||
_getFilteredLocalHosts() {
|
||||
if (this._filteredHosts === null) {
|
||||
this._filteredHosts = this._hosts.values()
|
||||
.filter(h => this._filter(h) && h.datacenter === this.localDc);
|
||||
}
|
||||
return this._filteredHosts;
|
||||
}
|
||||
|
||||
_getIndex() {
|
||||
const result = this._index++;
|
||||
// Overflow protection
|
||||
if (this._index === 0x7fffffff) {
|
||||
this._index = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
_sendUnhealthyToTheBack(replicas, unhealthyReplicas) {
|
||||
let counter = 0;
|
||||
|
||||
// Start from the back, move backwards and stop once all unhealthy replicas are at the back
|
||||
for (let i = replicas.length - 1; i >= 0 && counter < unhealthyReplicas; i--) {
|
||||
const host = replicas[i];
|
||||
if (this._healthCheck(host)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetIndex = replicas.length - 1 - counter;
|
||||
if (targetIndex !== i) {
|
||||
const temp = replicas[targetIndex];
|
||||
replicas[targetIndex] = host;
|
||||
replicas[i] = temp;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
_defaultFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
getOptions() {
|
||||
return new Map([
|
||||
['localDataCenter', this.localDc ],
|
||||
['filterFunction', this._filter !== this._defaultFilter ]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and sets the local data center to be used.
|
||||
* @param {LoadBalancingPolicy} lbp
|
||||
* @param {Client} client
|
||||
* @param {HostMap} hosts
|
||||
* @private
|
||||
*/
|
||||
function setLocalDc(lbp, client, hosts) {
|
||||
if (!(lbp instanceof LoadBalancingPolicy)) {
|
||||
throw new errors.DriverInternalError('LoadBalancingPolicy instance was not provided');
|
||||
}
|
||||
|
||||
if (client && client.options) {
|
||||
if (lbp.localDc && !client.options.localDataCenter) {
|
||||
client.log('info', `Local data center '${lbp.localDc}' was provided as an argument to the load-balancing` +
|
||||
` policy. It is preferable to specify the local data center using 'localDataCenter' in Client` +
|
||||
` options instead when your application is targeting a single data center.`);
|
||||
}
|
||||
|
||||
// If localDc is unset, use value set in client options.
|
||||
lbp.localDc = lbp.localDc || client.options.localDataCenter;
|
||||
}
|
||||
|
||||
const dcs = getDataCenters(hosts);
|
||||
|
||||
if (!lbp.localDc) {
|
||||
throw new errors.ArgumentError(
|
||||
`'localDataCenter' is not defined in Client options and also was not specified in constructor.` +
|
||||
` At least one is required. Available DCs are: [${Array.from(dcs)}]`);
|
||||
}
|
||||
|
||||
if (!dcs.has(lbp.localDc)) {
|
||||
throw new errors.ArgumentError(`Datacenter ${lbp.localDc} was not found. Available DCs are: [${Array.from(dcs)}]`);
|
||||
}
|
||||
}
|
||||
|
||||
function getDataCenters(hosts) {
|
||||
return new Set(hosts.values().map(h => h.datacenter));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AllowListPolicy,
|
||||
DCAwareRoundRobinPolicy,
|
||||
DefaultLoadBalancingPolicy,
|
||||
LoadBalancingPolicy,
|
||||
RoundRobinPolicy,
|
||||
TokenAwarePolicy,
|
||||
// Deprecated: for backward compatibility only.
|
||||
WhiteListPolicy
|
||||
};
|
157
node_modules/cassandra-driver/lib/policies/reconnection.js
generated
vendored
Normal file
157
node_modules/cassandra-driver/lib/policies/reconnection.js
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 util = require('util');
|
||||
|
||||
/** @module policies/reconnection */
|
||||
/**
|
||||
* Base class for Reconnection Policies
|
||||
* @constructor
|
||||
*/
|
||||
function ReconnectionPolicy() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A new reconnection schedule.
|
||||
* @returns {{next: function}} An infinite iterator
|
||||
*/
|
||||
ReconnectionPolicy.prototype.newSchedule = function () {
|
||||
throw new Error('You must implement a new schedule for the Reconnection class');
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
ReconnectionPolicy.prototype.getOptions = function () {
|
||||
return new Map();
|
||||
};
|
||||
|
||||
/**
|
||||
* A reconnection policy that waits a constant time between each reconnection attempt.
|
||||
* @param {Number} delay Delay in ms
|
||||
* @constructor
|
||||
*/
|
||||
function ConstantReconnectionPolicy(delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
util.inherits(ConstantReconnectionPolicy, ReconnectionPolicy);
|
||||
|
||||
/**
|
||||
* A new reconnection schedule that returns the same next delay value
|
||||
* @returns {{next: Function}} An infinite iterator
|
||||
*/
|
||||
ConstantReconnectionPolicy.prototype.newSchedule = function () {
|
||||
const self = this;
|
||||
return {
|
||||
next: function () {
|
||||
return {value: self.delay, done: false};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
ConstantReconnectionPolicy.prototype.getOptions = function () {
|
||||
return new Map([['delay', this.delay ]]);
|
||||
};
|
||||
|
||||
/**
|
||||
* A reconnection policy that waits exponentially longer between each
|
||||
* reconnection attempt (but keeps a constant delay once a maximum delay is reached).
|
||||
* <p>
|
||||
* A random amount of jitter (+/- 15%) will be added to the pure exponential delay value to avoid situations
|
||||
* where many clients are in the reconnection process at exactly the same time. The jitter will never cause the
|
||||
* delay to be less than the base delay, or more than the max delay.
|
||||
* </p>
|
||||
* @param {Number} baseDelay The base delay in milliseconds to use for the schedules created by this policy.
|
||||
* @param {Number} maxDelay The maximum delay in milliseconds to wait between two reconnection attempt.
|
||||
* @param {Boolean} startWithNoDelay Determines if the first attempt should be zero delay
|
||||
* @constructor
|
||||
*/
|
||||
function ExponentialReconnectionPolicy(baseDelay, maxDelay, startWithNoDelay) {
|
||||
this.baseDelay = baseDelay;
|
||||
this.maxDelay = maxDelay;
|
||||
this.startWithNoDelay = startWithNoDelay;
|
||||
}
|
||||
|
||||
util.inherits(ExponentialReconnectionPolicy, ReconnectionPolicy);
|
||||
|
||||
/**
|
||||
* A new schedule that uses an exponentially growing delay between reconnection attempts.
|
||||
* @returns {{next: Function}} An infinite iterator.
|
||||
*/
|
||||
ExponentialReconnectionPolicy.prototype.newSchedule = function* () {
|
||||
let index = this.startWithNoDelay ? -1 : 0;
|
||||
|
||||
while (true) {
|
||||
let delay = 0;
|
||||
|
||||
if (index >= 64) {
|
||||
delay = this.maxDelay;
|
||||
} else if (index !== -1) {
|
||||
delay = Math.min(Math.pow(2, index) * this.baseDelay, this.maxDelay);
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
yield this._addJitter(delay);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a random portion of +-15% to the delay provided.
|
||||
* Initially, its adds a random value of 15% to avoid reconnection before reaching the base delay.
|
||||
* When the schedule reaches max delay, only subtracts a random portion of 15%.
|
||||
*/
|
||||
ExponentialReconnectionPolicy.prototype._addJitter = function (value) {
|
||||
if (value === 0) {
|
||||
// Instant reconnection without jitter
|
||||
return value;
|
||||
}
|
||||
|
||||
// Use the formula: 85% + rnd() * 30% to calculate the percentage of the original delay
|
||||
let minPercentage = 0.85;
|
||||
let range = 0.30;
|
||||
|
||||
if (!this.startWithNoDelay && value === this.baseDelay) {
|
||||
// Between 100% to 115% of the original value
|
||||
minPercentage = 1;
|
||||
range = 0.15;
|
||||
} else if (value === this.maxDelay) {
|
||||
// Between 85% to 100% of the original value
|
||||
range = 0.15;
|
||||
}
|
||||
|
||||
return Math.floor(value * (Math.random() * range + minPercentage));
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
ExponentialReconnectionPolicy.prototype.getOptions = function () {
|
||||
return new Map([
|
||||
['baseDelay', this.baseDelay ],
|
||||
['maxDelay', this.maxDelay ],
|
||||
['startWithNoDelay', this.startWithNoDelay ]
|
||||
]);
|
||||
};
|
||||
|
||||
exports.ReconnectionPolicy = ReconnectionPolicy;
|
||||
exports.ConstantReconnectionPolicy = ConstantReconnectionPolicy;
|
||||
exports.ExponentialReconnectionPolicy = ExponentialReconnectionPolicy;
|
276
node_modules/cassandra-driver/lib/policies/retry.js
generated
vendored
Normal file
276
node_modules/cassandra-driver/lib/policies/retry.js
generated
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* 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 util = require('util');
|
||||
|
||||
|
||||
/** @module policies/retry */
|
||||
/**
|
||||
* Base and default RetryPolicy.
|
||||
* Determines what to do when the drivers runs into an specific Cassandra exception
|
||||
* @constructor
|
||||
*/
|
||||
function RetryPolicy() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what to do when the driver gets an UnavailableException response from a Cassandra node.
|
||||
* @param {OperationInfo} info
|
||||
* @param {Number} consistency The [consistency]{@link module:types~consistencies} level of the query that triggered
|
||||
* the exception.
|
||||
* @param {Number} required The number of replicas whose response is required to achieve the
|
||||
* required [consistency]{@link module:types~consistencies}.
|
||||
* @param {Number} alive The number of replicas that were known to be alive when the request had been processed
|
||||
* (since an unavailable exception has been triggered, there will be alive < required)
|
||||
* @returns {DecisionInfo}
|
||||
*/
|
||||
RetryPolicy.prototype.onUnavailable = function (info, consistency, required, alive) {
|
||||
if (info.nbRetry > 0) {
|
||||
return this.rethrowResult();
|
||||
}
|
||||
return this.retryResult(undefined, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines what to do when the driver gets a ReadTimeoutException response from a Cassandra node.
|
||||
* @param {OperationInfo} info
|
||||
* @param {Number} consistency The [consistency]{@link module:types~consistencies} level of the query that triggered
|
||||
* the exception.
|
||||
* @param {Number} received The number of nodes having answered the request.
|
||||
* @param {Number} blockFor The number of replicas whose response is required to achieve the
|
||||
* required [consistency]{@link module:types~consistencies}.
|
||||
* @param {Boolean} isDataPresent When <code>false</code>, it means the replica that was asked for data has not responded.
|
||||
* @returns {DecisionInfo}
|
||||
*/
|
||||
RetryPolicy.prototype.onReadTimeout = function (info, consistency, received, blockFor, isDataPresent) {
|
||||
if (info.nbRetry > 0) {
|
||||
return this.rethrowResult();
|
||||
}
|
||||
return ((received >= blockFor && !isDataPresent) ?
|
||||
this.retryResult() :
|
||||
this.rethrowResult());
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines what to do when the driver gets a WriteTimeoutException response from a Cassandra node.
|
||||
* @param {OperationInfo} info
|
||||
* @param {Number} consistency The [consistency]{@link module:types~consistencies} level of the query that triggered
|
||||
* the exception.
|
||||
* @param {Number} received The number of nodes having acknowledged the request.
|
||||
* @param {Number} blockFor The number of replicas whose acknowledgement is required to achieve the required
|
||||
* [consistency]{@link module:types~consistencies}.
|
||||
* @param {String} writeType A <code>string</code> that describes the type of the write that timed out ("SIMPLE"
|
||||
* / "BATCH" / "BATCH_LOG" / "UNLOGGED_BATCH" / "COUNTER").
|
||||
* @returns {DecisionInfo}
|
||||
*/
|
||||
RetryPolicy.prototype.onWriteTimeout = function (info, consistency, received, blockFor, writeType) {
|
||||
if (info.nbRetry > 0) {
|
||||
return this.rethrowResult();
|
||||
}
|
||||
// If the batch log write failed, retry the operation as this might just be we were unlucky at picking candidates
|
||||
return writeType === "BATCH_LOG" ? this.retryResult() : this.rethrowResult();
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines whether to retry and at which consistency level on an unexpected error.
|
||||
* <p>
|
||||
* This method might be invoked in the following situations:
|
||||
* </p>
|
||||
* <ol>
|
||||
* <li>On a client timeout, while waiting for the server response
|
||||
* (see [socketOptions.readTimeout]{@link ClientOptions}), being the error an instance of
|
||||
* [OperationTimedOutError]{@link module:errors~OperationTimedOutError}.</li>
|
||||
* <li>On a connection error (socket closed, etc.).</li>
|
||||
* <li>When the contacted host replies with an error, such as <code>overloaded</code>, <code>isBootstrapping</code>,
|
||||
* </code>serverError, etc. In this case, the error is instance of [ResponseError]{@link module:errors~ResponseError}.
|
||||
* </li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* Note that when this method is invoked, <em>the driver cannot guarantee that the mutation has been effectively
|
||||
* applied server-side</em>; a retry should only be attempted if the request is known to be idempotent.
|
||||
* </p>
|
||||
* @param {OperationInfo} info
|
||||
* @param {Number|undefined} consistency The [consistency]{@link module:types~consistencies} level of the query that triggered
|
||||
* the exception.
|
||||
* @param {Error} err The error that caused this request to fail.
|
||||
* @returns {DecisionInfo}
|
||||
*/
|
||||
RetryPolicy.prototype.onRequestError = function (info, consistency, err) {
|
||||
// The default implementation triggers a retry on the next host in the query plan with the same consistency level,
|
||||
// regardless of the statement's idempotence, for historical reasons.
|
||||
return this.retryResult(undefined, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a {@link DecisionInfo} to retry the request with the given [consistency]{@link module:types~consistencies}.
|
||||
* @param {Number|undefined} [consistency] When specified, it retries the request with the given consistency.
|
||||
* @param {Boolean} [useCurrentHost] When specified, determines if the retry should be made using the same coordinator.
|
||||
* Default: true.
|
||||
* @returns {DecisionInfo}
|
||||
*/
|
||||
RetryPolicy.prototype.retryResult = function (consistency, useCurrentHost) {
|
||||
return {
|
||||
decision: RetryPolicy.retryDecision.retry,
|
||||
consistency: consistency,
|
||||
useCurrentHost: useCurrentHost !== false
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a {@link DecisionInfo} to callback in error when a err is obtained for a given request.
|
||||
* @returns {DecisionInfo}
|
||||
*/
|
||||
RetryPolicy.prototype.rethrowResult = function () {
|
||||
return { decision: RetryPolicy.retryDecision.rethrow };
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines the retry decision for the retry policies.
|
||||
* @type {Object}
|
||||
* @property {Number} rethrow
|
||||
* @property {Number} retry
|
||||
* @property {Number} ignore
|
||||
* @static
|
||||
*/
|
||||
RetryPolicy.retryDecision = {
|
||||
rethrow: 0,
|
||||
retry: 1,
|
||||
ignore: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>IdempotenceAwareRetryPolicy</code>.
|
||||
* @classdesc
|
||||
* A retry policy that avoids retrying non-idempotent statements.
|
||||
* <p>
|
||||
* In case of write timeouts or unexpected errors, this policy will always return
|
||||
* [rethrowResult()]{@link module:policies/retry~RetryPolicy#rethrowResult} if the statement is deemed non-idempotent
|
||||
* (see [QueryOptions.isIdempotent]{@link QueryOptions}).
|
||||
* <p/>
|
||||
* For all other cases, this policy delegates the decision to the child policy.
|
||||
* @param {RetryPolicy} [childPolicy] The child retry policy to wrap. When not defined, it will use an instance of
|
||||
* [RetryPolicy]{@link module:policies/retry~RetryPolicy} as child policy.
|
||||
* @extends module:policies/retry~RetryPolicy
|
||||
* @constructor
|
||||
* @deprecated Since version 4.0 non-idempotent operations are never tried for write timeout or request error, use the
|
||||
* default retry policy instead.
|
||||
*/
|
||||
function IdempotenceAwareRetryPolicy(childPolicy) {
|
||||
this._childPolicy = childPolicy || new RetryPolicy();
|
||||
}
|
||||
|
||||
util.inherits(IdempotenceAwareRetryPolicy, RetryPolicy);
|
||||
|
||||
IdempotenceAwareRetryPolicy.prototype.onReadTimeout = function (info, consistency, received, blockFor, isDataPresent) {
|
||||
return this._childPolicy.onReadTimeout(info, consistency, received, blockFor, isDataPresent);
|
||||
};
|
||||
|
||||
/**
|
||||
* If the query is not idempotent, it returns a rethrow decision. Otherwise, it relies on the child policy to decide.
|
||||
*/
|
||||
IdempotenceAwareRetryPolicy.prototype.onRequestError = function (info, consistency, err) {
|
||||
if (info.executionOptions.isIdempotent()) {
|
||||
return this._childPolicy.onRequestError(info, consistency, err);
|
||||
}
|
||||
return this.rethrowResult();
|
||||
};
|
||||
|
||||
IdempotenceAwareRetryPolicy.prototype.onUnavailable = function (info, consistency, required, alive) {
|
||||
return this._childPolicy.onUnavailable(info, consistency, required, alive);
|
||||
};
|
||||
|
||||
/**
|
||||
* If the query is not idempotent, it return a rethrow decision. Otherwise, it relies on the child policy to decide.
|
||||
*/
|
||||
IdempotenceAwareRetryPolicy.prototype.onWriteTimeout = function (info, consistency, received, blockFor, writeType) {
|
||||
if (info.executionOptions.isIdempotent()) {
|
||||
return this._childPolicy.onWriteTimeout(info, consistency, received, blockFor, writeType);
|
||||
}
|
||||
return this.rethrowResult();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new instance of FallthroughRetryPolicy.
|
||||
* @classdesc
|
||||
* A retry policy that never retries nor ignores.
|
||||
* <p>
|
||||
* All of the methods of this retry policy unconditionally return
|
||||
* [rethrow]{@link module:policies/retry~Retry#rethrowResult()}. If this policy is used, retry logic will have to be
|
||||
* implemented in business code.
|
||||
* </p>
|
||||
* @alias module:policies/retry~FallthroughRetryPolicy
|
||||
* @extends RetryPolicy
|
||||
* @constructor
|
||||
*/
|
||||
function FallthroughRetryPolicy() {
|
||||
|
||||
}
|
||||
|
||||
util.inherits(FallthroughRetryPolicy, RetryPolicy);
|
||||
|
||||
/**
|
||||
* Implementation of RetryPolicy method that returns [rethrow]{@link module:policies/retry~Retry#rethrowResult()}.
|
||||
*/
|
||||
FallthroughRetryPolicy.prototype.onReadTimeout = function () {
|
||||
return this.rethrowResult();
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of RetryPolicy method that returns [rethrow]{@link module:policies/retry~Retry#rethrowResult()}.
|
||||
*/
|
||||
FallthroughRetryPolicy.prototype.onRequestError = function () {
|
||||
return this.rethrowResult();
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of RetryPolicy method that returns [rethrow]{@link module:policies/retry~Retry#rethrowResult()}.
|
||||
*/
|
||||
FallthroughRetryPolicy.prototype.onUnavailable = function () {
|
||||
return this.rethrowResult();
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of RetryPolicy method that returns [rethrow]{@link module:policies/retry~Retry#rethrowResult()}.
|
||||
*/
|
||||
FallthroughRetryPolicy.prototype.onWriteTimeout = function () {
|
||||
return this.rethrowResult();
|
||||
};
|
||||
|
||||
/**
|
||||
* Decision information
|
||||
* @typedef {Object} DecisionInfo
|
||||
* @property {Number} decision The decision as specified in
|
||||
* [retryDecision]{@link module:policies/retry~RetryPolicy.retryDecision}.
|
||||
* @property {Number} [consistency] The [consistency level]{@link module:types~consistencies}.
|
||||
* @property {useCurrentHost} [useCurrentHost] Determines if it should use the same host to retry the request.
|
||||
* <p>
|
||||
* In the case that the current host is not available anymore, it will be retried on the next host even when
|
||||
* <code>useCurrentHost</code> is set to <code>true</code>.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Information of the execution to be used to determine whether the operation should be retried.
|
||||
* @typedef {Object} OperationInfo
|
||||
* @property {String} query The query that was executed.
|
||||
* @param {ExecutionOptions} executionOptions The options related to the execution of the request.
|
||||
* @property {Number} nbRetry The number of retries already performed for this operation.
|
||||
*/
|
||||
|
||||
exports.IdempotenceAwareRetryPolicy = IdempotenceAwareRetryPolicy;
|
||||
exports.FallthroughRetryPolicy = FallthroughRetryPolicy;
|
||||
exports.RetryPolicy = RetryPolicy;
|
143
node_modules/cassandra-driver/lib/policies/speculative-execution.js
generated
vendored
Normal file
143
node_modules/cassandra-driver/lib/policies/speculative-execution.js
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 util = require('util');
|
||||
const errors = require('../errors');
|
||||
|
||||
/** @module policies/speculativeExecution */
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* The policy that decides if the driver will send speculative queries to the next hosts when the current host takes too
|
||||
* long to respond.
|
||||
* <p>Note that only idempotent statements will be speculatively retried.</p>
|
||||
* @constructor
|
||||
* @abstract
|
||||
*/
|
||||
function SpeculativeExecutionPolicy() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization method that gets invoked on Client startup.
|
||||
* @param {Client} client
|
||||
* @abstract
|
||||
*/
|
||||
SpeculativeExecutionPolicy.prototype.init = function (client) {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets invoked at client shutdown, giving the opportunity to the implementor to perform cleanup.
|
||||
* @abstract
|
||||
*/
|
||||
SpeculativeExecutionPolicy.prototype.shutdown = function () {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the plan to use for a new query.
|
||||
* Returns an object with a <code>nextExecution()</code> method, which returns a positive number representing the
|
||||
* amount of milliseconds to delay the next execution or a non-negative number to avoid further executions.
|
||||
* @param {String} keyspace The currently logged keyspace.
|
||||
* @param {String|Array<String>} queryInfo The query, or queries in the case of batches, for which to build a plan.
|
||||
* @return {{nextExecution: function}}
|
||||
* @abstract
|
||||
*/
|
||||
SpeculativeExecutionPolicy.prototype.newPlan = function (keyspace, queryInfo) {
|
||||
throw new Error('You must implement newPlan() method in the SpeculativeExecutionPolicy');
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
SpeculativeExecutionPolicy.prototype.getOptions = function () {
|
||||
return new Map();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new instance of NoSpeculativeExecutionPolicy.
|
||||
* @classdesc
|
||||
* A {@link SpeculativeExecutionPolicy} that never schedules speculative executions.
|
||||
* @constructor
|
||||
* @extends {SpeculativeExecutionPolicy}
|
||||
*/
|
||||
function NoSpeculativeExecutionPolicy() {
|
||||
this._plan = {
|
||||
nextExecution: function () {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(NoSpeculativeExecutionPolicy, SpeculativeExecutionPolicy);
|
||||
|
||||
NoSpeculativeExecutionPolicy.prototype.newPlan = function () {
|
||||
return this._plan;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of ConstantSpeculativeExecutionPolicy.
|
||||
* @classdesc
|
||||
* A {@link SpeculativeExecutionPolicy} that schedules a given number of speculative executions,
|
||||
* separated by a fixed delay.
|
||||
* @constructor
|
||||
* @param {Number} delay The delay between each speculative execution.
|
||||
* @param {Number} maxSpeculativeExecutions The amount of speculative executions that should be scheduled after the
|
||||
* initial execution. Must be strictly positive.
|
||||
* @extends {SpeculativeExecutionPolicy}
|
||||
*/
|
||||
function ConstantSpeculativeExecutionPolicy(delay, maxSpeculativeExecutions) {
|
||||
if (!(delay >= 0)) {
|
||||
throw new errors.ArgumentError('delay must be a positive number or zero');
|
||||
}
|
||||
if (!(maxSpeculativeExecutions > 0)) {
|
||||
throw new errors.ArgumentError('maxSpeculativeExecutions must be a positive number');
|
||||
}
|
||||
this._delay = delay;
|
||||
this._maxSpeculativeExecutions = maxSpeculativeExecutions;
|
||||
}
|
||||
|
||||
util.inherits(ConstantSpeculativeExecutionPolicy, SpeculativeExecutionPolicy);
|
||||
|
||||
ConstantSpeculativeExecutionPolicy.prototype.newPlan = function () {
|
||||
let executions = 0;
|
||||
const self = this;
|
||||
return {
|
||||
nextExecution: function () {
|
||||
if (executions++ < self._maxSpeculativeExecutions) {
|
||||
return self._delay;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an associative array containing the policy options.
|
||||
*/
|
||||
ConstantSpeculativeExecutionPolicy.prototype.getOptions = function () {
|
||||
return new Map([
|
||||
['delay', this._delay ],
|
||||
['maxSpeculativeExecutions', this._maxSpeculativeExecutions ]
|
||||
]);
|
||||
};
|
||||
|
||||
exports.NoSpeculativeExecutionPolicy = NoSpeculativeExecutionPolicy;
|
||||
exports.SpeculativeExecutionPolicy = SpeculativeExecutionPolicy;
|
||||
exports.ConstantSpeculativeExecutionPolicy = ConstantSpeculativeExecutionPolicy;
|
170
node_modules/cassandra-driver/lib/policies/timestamp-generation.js
generated
vendored
Normal file
170
node_modules/cassandra-driver/lib/policies/timestamp-generation.js
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 util = require('util');
|
||||
const { Long } = require('../types');
|
||||
const errors = require('../errors');
|
||||
|
||||
/** @module policies/timestampGeneration */
|
||||
|
||||
/**
|
||||
* Defines the maximum date in milliseconds that can be represented in microseconds using Number ((2 ^ 53) / 1000)
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
const _maxSafeNumberDate = 9007199254740;
|
||||
|
||||
/**
|
||||
* A long representing the value 1000
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
const _longOneThousand = Long.fromInt(1000);
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link TimestampGenerator}.
|
||||
* @classdesc
|
||||
* Generates client-side, microsecond-precision query timestamps.
|
||||
* <p>
|
||||
* Given that Cassandra uses those timestamps to resolve conflicts, implementations should generate
|
||||
* monotonically increasing timestamps for successive invocations of {@link TimestampGenerator.next()}.
|
||||
* </p>
|
||||
* @constructor
|
||||
*/
|
||||
function TimestampGenerator() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next timestamp.
|
||||
* <p>
|
||||
* Implementors should enforce increasing monotonicity of timestamps, that is,
|
||||
* a timestamp returned should always be strictly greater that any previously returned
|
||||
* timestamp.
|
||||
* <p/>
|
||||
* <p>
|
||||
* Implementors should strive to achieve microsecond precision in the best possible way,
|
||||
* which is usually largely dependent on the underlying operating system's capabilities.
|
||||
* </p>
|
||||
* @param {Client} client The {@link Client} instance to generate timestamps to.
|
||||
* @returns {Long|Number|null} the next timestamp (in microseconds). If it's equals to <code>null</code>, it won't be
|
||||
* sent by the driver, letting the server to generate the timestamp.
|
||||
* @abstract
|
||||
*/
|
||||
TimestampGenerator.prototype.next = function (client) {
|
||||
throw new Error('next() must be implemented');
|
||||
};
|
||||
|
||||
/**
|
||||
* A timestamp generator that guarantees monotonically increasing timestamps and logs warnings when timestamps
|
||||
* drift in the future.
|
||||
* <p>
|
||||
* {@link Date} has millisecond precision and client timestamps require microsecond precision. This generator
|
||||
* keeps track of the last generated timestamp, and if the current time is within the same millisecond as the last,
|
||||
* it fills the microsecond portion of the new timestamp with the value of an incrementing counter.
|
||||
* </p>
|
||||
* @param {Number} [warningThreshold] Determines how far in the future timestamps are allowed to drift before a
|
||||
* warning is logged, expressed in milliseconds. Default: <code>1000</code>.
|
||||
* @param {Number} [minLogInterval] In case of multiple log events, it determines the time separation between log
|
||||
* events, expressed in milliseconds. Use 0 to disable. Default: <code>1000</code>.
|
||||
* @extends {TimestampGenerator}
|
||||
* @constructor
|
||||
*/
|
||||
function MonotonicTimestampGenerator(warningThreshold, minLogInterval) {
|
||||
if (warningThreshold < 0) {
|
||||
throw new errors.ArgumentError('warningThreshold can not be lower than 0');
|
||||
}
|
||||
this._warningThreshold = warningThreshold || 1000;
|
||||
this._minLogInterval = 1000;
|
||||
if (typeof minLogInterval === 'number') {
|
||||
// A value under 1 will disable logging
|
||||
this._minLogInterval = minLogInterval;
|
||||
}
|
||||
this._micros = -1;
|
||||
this._lastDate = 0;
|
||||
this._lastLogDate = 0;
|
||||
}
|
||||
|
||||
util.inherits(MonotonicTimestampGenerator, TimestampGenerator);
|
||||
|
||||
/**
|
||||
* Returns the current time in milliseconds since UNIX epoch
|
||||
* @returns {Number}
|
||||
*/
|
||||
MonotonicTimestampGenerator.prototype.getDate = function () {
|
||||
return Date.now();
|
||||
};
|
||||
|
||||
MonotonicTimestampGenerator.prototype.next = function (client) {
|
||||
let date = this.getDate();
|
||||
let drifted = 0;
|
||||
if (date > this._lastDate) {
|
||||
this._micros = 0;
|
||||
this._lastDate = date;
|
||||
return this._generateMicroseconds();
|
||||
}
|
||||
|
||||
if (date < this._lastDate) {
|
||||
drifted = this._lastDate - date;
|
||||
date = this._lastDate;
|
||||
}
|
||||
if (++this._micros === 1000) {
|
||||
this._micros = 0;
|
||||
if (date === this._lastDate) {
|
||||
// Move date 1 millisecond into the future
|
||||
date++;
|
||||
drifted++;
|
||||
}
|
||||
}
|
||||
const lastDate = this._lastDate;
|
||||
this._lastDate = date;
|
||||
const result = this._generateMicroseconds();
|
||||
if (drifted >= this._warningThreshold) {
|
||||
// Avoid logging an unbounded amount of times within a clock-skew event or during an interval when more than 1
|
||||
// query is being issued by microsecond
|
||||
const currentLogDate = Date.now();
|
||||
if (this._minLogInterval > 0 && this._lastLogDate + this._minLogInterval <= currentLogDate){
|
||||
const message = util.format(
|
||||
'Timestamp generated using current date was %d milliseconds behind the last generated timestamp (which ' +
|
||||
'millisecond portion was %d), the returned value (%s) is being artificially incremented to guarantee ' +
|
||||
'monotonicity.',
|
||||
drifted, lastDate, result);
|
||||
this._lastLogDate = currentLogDate;
|
||||
client.log('warning', message);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @returns {Number|Long}
|
||||
*/
|
||||
MonotonicTimestampGenerator.prototype._generateMicroseconds = function () {
|
||||
if (this._lastDate < _maxSafeNumberDate) {
|
||||
// We are safe until Jun 06 2255, its faster to perform this operations on Number than on Long
|
||||
// We hope to have native int64 by then :)
|
||||
return this._lastDate * 1000 + this._micros;
|
||||
}
|
||||
return Long
|
||||
.fromNumber(this._lastDate)
|
||||
.multiply(_longOneThousand)
|
||||
.add(Long.fromInt(this._micros));
|
||||
};
|
||||
|
||||
exports.TimestampGenerator = TimestampGenerator;
|
||||
exports.MonotonicTimestampGenerator = MonotonicTimestampGenerator;
|
Reference in New Issue
Block a user