/*
* 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 false
, 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 string
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.
*
* This method might be invoked in the following situations: *
*overloaded
, isBootstrapping
,
* serverError, etc. In this case, the error is instance of [ResponseError]{@link module:errors~ResponseError}.
* * Note that when this method is invoked, the driver cannot guarantee that the mutation has been effectively * applied server-side; a retry should only be attempted if the request is known to be idempotent. *
* @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 ofIdempotenceAwareRetryPolicy
.
* @classdesc
* A retry policy that avoids retrying non-idempotent statements.
* * 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}). *
* 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. ** 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. *
* @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. *
* In the case that the current host is not available anymore, it will be retried on the next host even when
* useCurrentHost
is set to true
.
*