-
Notifications
You must be signed in to change notification settings - Fork 196
/
retry.js
276 lines (250 loc) · 11 KB
/
retry.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
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;