@ragestudio/scylla-odm 0.22.2 → 0.22.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/batch/index.d.ts +3 -3
- package/batch/index.d.ts.map +1 -1
- package/client.d.ts +6 -5
- package/client.d.ts.map +1 -1
- package/client.js +7 -7
- package/client.js.map +1 -1
- package/cql_gen/create_table.d.ts +1 -1
- package/cql_gen/create_table.d.ts.map +1 -1
- package/document/index.d.ts +3 -3
- package/document/index.d.ts.map +1 -1
- package/driver/LICENSE.txt +177 -0
- package/driver/NOTICE.txt +67 -0
- package/driver/auth/index.d.ts +37 -0
- package/driver/auth/index.js +37 -0
- package/driver/auth/no-auth-provider.js +73 -0
- package/driver/auth/plain-text-auth-provider.js +81 -0
- package/driver/auth/provider.js +77 -0
- package/driver/client-options.js +442 -0
- package/driver/client.js +1267 -0
- package/driver/concurrent/index.d.ts +49 -0
- package/driver/concurrent/index.js +366 -0
- package/driver/connection.js +1034 -0
- package/driver/control-connection.js +1282 -0
- package/driver/encoder.js +2316 -0
- package/driver/errors.js +223 -0
- package/driver/execution-options.js +612 -0
- package/driver/execution-profile.js +274 -0
- package/driver/host-connection-pool.js +587 -0
- package/driver/host.js +699 -0
- package/driver/index.d.ts +387 -0
- package/driver/index.js +81 -0
- package/driver/mapping/cache.js +214 -0
- package/driver/mapping/doc-info-adapter.js +171 -0
- package/driver/mapping/index.d.ts +219 -0
- package/driver/mapping/index.js +57 -0
- package/driver/mapping/mapper.js +225 -0
- package/driver/mapping/mapping-handler.js +641 -0
- package/driver/mapping/model-batch-item.js +215 -0
- package/driver/mapping/model-batch-mapper.js +141 -0
- package/driver/mapping/model-mapper.js +315 -0
- package/driver/mapping/model-mapping-info.js +225 -0
- package/driver/mapping/object-selector.js +417 -0
- package/driver/mapping/q.js +156 -0
- package/driver/mapping/query-generator.js +556 -0
- package/driver/mapping/result-mapper.js +123 -0
- package/driver/mapping/result.js +139 -0
- package/driver/mapping/table-mappings.js +133 -0
- package/driver/mapping/tree.js +160 -0
- package/driver/metadata/aggregate.js +79 -0
- package/driver/metadata/client-state.js +119 -0
- package/driver/metadata/data-collection.js +182 -0
- package/driver/metadata/event-debouncer.js +174 -0
- package/driver/metadata/index.d.ts +276 -0
- package/driver/metadata/index.js +1156 -0
- package/driver/metadata/materialized-view.js +49 -0
- package/driver/metadata/schema-function.js +98 -0
- package/driver/metadata/schema-index.js +166 -0
- package/driver/metadata/schema-parser.js +1399 -0
- package/driver/metadata/table-metadata.js +77 -0
- package/driver/operation-state.js +206 -0
- package/driver/policies/address-resolution.js +145 -0
- package/driver/policies/index.d.ts +241 -0
- package/driver/policies/index.js +110 -0
- package/driver/policies/load-balancing.js +970 -0
- package/driver/policies/reconnection.js +166 -0
- package/driver/policies/retry.js +326 -0
- package/driver/policies/speculative-execution.js +150 -0
- package/driver/policies/timestamp-generation.js +176 -0
- package/driver/prepare-handler.js +347 -0
- package/driver/promise-utils.js +191 -0
- package/driver/readers.js +624 -0
- package/driver/request-execution.js +644 -0
- package/driver/request-handler.js +332 -0
- package/driver/requests.js +618 -0
- package/driver/stream-id-stack.js +209 -0
- package/driver/streams.js +745 -0
- package/driver/token.js +325 -0
- package/driver/tokenizer.js +631 -0
- package/driver/types/big-decimal.js +282 -0
- package/driver/types/duration.js +576 -0
- package/driver/types/index.d.ts +486 -0
- package/driver/types/index.js +733 -0
- package/driver/types/inet-address.js +262 -0
- package/driver/types/integer.js +818 -0
- package/driver/types/local-date.js +280 -0
- package/driver/types/local-time.js +299 -0
- package/driver/types/mutable-long.js +385 -0
- package/driver/types/protocol-version.js +391 -0
- package/driver/types/result-set.js +287 -0
- package/driver/types/result-stream.js +164 -0
- package/driver/types/row.js +85 -0
- package/driver/types/time-uuid.js +414 -0
- package/driver/types/tuple.js +103 -0
- package/driver/types/uuid.js +160 -0
- package/driver/types/vector.js +130 -0
- package/driver/types/version-number.js +153 -0
- package/driver/utils.js +1485 -0
- package/driver/writers.js +350 -0
- package/global.d.ts +1 -1
- package/global.d.ts.map +1 -1
- package/index.d.ts +6 -6
- package/index.d.ts.map +1 -1
- package/index.js +6 -6
- package/index.js.map +1 -1
- package/migrate/index.d.ts +1 -1
- package/migrate/index.d.ts.map +1 -1
- package/migrate/index.js +1 -1
- package/migrate/index.js.map +1 -1
- package/model/index.d.ts +6 -6
- package/model/index.d.ts.map +1 -1
- package/model/index.js +10 -10
- package/model/index.js.map +1 -1
- package/operations/countAll.d.ts +1 -1
- package/operations/countAll.d.ts.map +1 -1
- package/operations/delete.d.ts +3 -4
- package/operations/delete.d.ts.map +1 -1
- package/operations/delete.js +1 -1
- package/operations/delete.js.map +1 -1
- package/operations/find.d.ts +2 -2
- package/operations/find.d.ts.map +1 -1
- package/operations/find.js +1 -1
- package/operations/find.js.map +1 -1
- package/operations/findOne.d.ts +2 -2
- package/operations/findOne.d.ts.map +1 -1
- package/operations/findOne.js +1 -1
- package/operations/findOne.js.map +1 -1
- package/operations/insert.d.ts +3 -3
- package/operations/insert.d.ts.map +1 -1
- package/operations/insert.js +2 -2
- package/operations/insert.js.map +1 -1
- package/operations/sync.d.ts +1 -1
- package/operations/sync.d.ts.map +1 -1
- package/operations/sync.js +1 -1
- package/operations/sync.js.map +1 -1
- package/operations/tableExists.d.ts +1 -1
- package/operations/tableExists.d.ts.map +1 -1
- package/operations/update.d.ts +3 -3
- package/operations/update.d.ts.map +1 -1
- package/operations/update.js +2 -2
- package/operations/update.js.map +1 -1
- package/package.json +4 -12
- package/schema/index.d.ts +1 -1
- package/schema/index.d.ts.map +1 -1
- package/types.d.ts +4 -4
- package/types.d.ts.map +1 -1
- package/utils/queryParser.d.ts +1 -1
- package/utils/queryParser.d.ts.map +1 -1
- package/utils/queryParser.js +1 -1
- package/utils/queryParser.js.map +1 -1
- package/utils/typeChecker.d.ts +1 -1
- package/utils/typeChecker.d.ts.map +1 -1
- package/utils/typeChecker.js +1 -1
- package/utils/typeChecker.js.map +1 -1
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
import errors from "./errors.js"
|
|
19
|
+
import requests from "./requests.js"
|
|
20
|
+
import retry from "./policies/retry.js"
|
|
21
|
+
import types from "./types/index.js"
|
|
22
|
+
import utils from "./utils.js"
|
|
23
|
+
import promiseUtils from "./promise-utils.js"
|
|
24
|
+
|
|
25
|
+
const retryOnCurrentHost = Object.freeze({
|
|
26
|
+
decision: retry.RetryPolicy.retryDecision.retry,
|
|
27
|
+
useCurrentHost: true,
|
|
28
|
+
consistency: undefined,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const rethrowDecision = Object.freeze({
|
|
32
|
+
decision: retry.RetryPolicy.retryDecision.rethrow,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* An internal representation of an error that occurred during the execution of a request.
|
|
37
|
+
*/
|
|
38
|
+
const errorCodes = {
|
|
39
|
+
none: 0,
|
|
40
|
+
// Socket error
|
|
41
|
+
socketError: 1,
|
|
42
|
+
// Socket error before the request was written to the wire
|
|
43
|
+
socketErrorBeforeRequestWritten: 2,
|
|
44
|
+
// OperationTimedOutError
|
|
45
|
+
clientTimeout: 3,
|
|
46
|
+
// Response error "unprepared"
|
|
47
|
+
serverErrorUnprepared: 4,
|
|
48
|
+
// Response error "overloaded", "is_bootstrapping" and "truncateError":
|
|
49
|
+
serverErrorOverloaded: 5,
|
|
50
|
+
serverErrorReadTimeout: 6,
|
|
51
|
+
serverErrorUnavailable: 7,
|
|
52
|
+
serverErrorWriteTimeout: 8,
|
|
53
|
+
// Any other server error (different from the ones detailed above)
|
|
54
|
+
serverErrorOther: 9,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const metricsHandlers = new Map([
|
|
58
|
+
[
|
|
59
|
+
errorCodes.none,
|
|
60
|
+
(metrics, err, latency) => metrics.onSuccessfulResponse(latency),
|
|
61
|
+
],
|
|
62
|
+
[errorCodes.socketError, (metrics, err) => metrics.onConnectionError(err)],
|
|
63
|
+
[
|
|
64
|
+
errorCodes.clientTimeout,
|
|
65
|
+
(metrics, err) => metrics.onClientTimeoutError(err),
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
errorCodes.serverErrorOverloaded,
|
|
69
|
+
(metrics, err) => metrics.onOtherError(err),
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
errorCodes.serverErrorReadTimeout,
|
|
73
|
+
(metrics, err) => metrics.onReadTimeoutError(err),
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
errorCodes.serverErrorUnavailable,
|
|
77
|
+
(metrics, err) => metrics.onUnavailableError(err),
|
|
78
|
+
],
|
|
79
|
+
[
|
|
80
|
+
errorCodes.serverErrorWriteTimeout,
|
|
81
|
+
(metrics, err) => metrics.onWriteTimeoutError(err),
|
|
82
|
+
],
|
|
83
|
+
[errorCodes.serverErrorOther, (metrics, err) => metrics.onOtherError(err)],
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
const metricsRetryHandlers = new Map([
|
|
87
|
+
[errorCodes.socketError, (metrics, err) => metrics.onOtherErrorRetry(err)],
|
|
88
|
+
[
|
|
89
|
+
errorCodes.clientTimeout,
|
|
90
|
+
(metrics, err) => metrics.onClientTimeoutRetry(err),
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
errorCodes.serverErrorOverloaded,
|
|
94
|
+
(metrics, err) => metrics.onOtherErrorRetry(err),
|
|
95
|
+
],
|
|
96
|
+
[
|
|
97
|
+
errorCodes.serverErrorReadTimeout,
|
|
98
|
+
(metrics, err) => metrics.onReadTimeoutRetry(err),
|
|
99
|
+
],
|
|
100
|
+
[
|
|
101
|
+
errorCodes.serverErrorUnavailable,
|
|
102
|
+
(metrics, err) => metrics.onUnavailableRetry(err),
|
|
103
|
+
],
|
|
104
|
+
[
|
|
105
|
+
errorCodes.serverErrorWriteTimeout,
|
|
106
|
+
(metrics, err) => metrics.onWriteTimeoutRetry(err),
|
|
107
|
+
],
|
|
108
|
+
[
|
|
109
|
+
errorCodes.serverErrorOther,
|
|
110
|
+
(metrics, err) => metrics.onOtherErrorRetry(err),
|
|
111
|
+
],
|
|
112
|
+
])
|
|
113
|
+
|
|
114
|
+
class RequestExecution {
|
|
115
|
+
/**
|
|
116
|
+
* Encapsulates a single flow of execution against a coordinator, handling individual retries and failover.
|
|
117
|
+
* @param {RequestHandler!} parent
|
|
118
|
+
* @param {Host!} host
|
|
119
|
+
* @param {Connection!} connection
|
|
120
|
+
*/
|
|
121
|
+
constructor(parent, host, connection) {
|
|
122
|
+
this._parent = parent
|
|
123
|
+
/** @type {OperationState} */
|
|
124
|
+
this._operation = null
|
|
125
|
+
this._host = host
|
|
126
|
+
this._connection = connection
|
|
127
|
+
this._cancelled = false
|
|
128
|
+
this._startTime = null
|
|
129
|
+
this._retryCount = 0
|
|
130
|
+
// The streamId information is not included in the request.
|
|
131
|
+
// A pointer to the parent request can be used, except when changing the consistency level from the retry policy
|
|
132
|
+
this._request = this._parent.request
|
|
133
|
+
|
|
134
|
+
// Mark that it launched a new execution
|
|
135
|
+
parent.speculativeExecutions++
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Sends the request using the active connection.
|
|
140
|
+
*/
|
|
141
|
+
start() {
|
|
142
|
+
this._sendOnConnection()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Borrows the next connection available using the query plan and sends the request.
|
|
147
|
+
* @returns {Promise<void>}
|
|
148
|
+
*/
|
|
149
|
+
async restart() {
|
|
150
|
+
try {
|
|
151
|
+
const { host, connection } = this._parent.getNextConnection()
|
|
152
|
+
|
|
153
|
+
this._connection = connection
|
|
154
|
+
this._host = host
|
|
155
|
+
} catch (err) {
|
|
156
|
+
return this._parent.handleNoHostAvailable(err, this)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// It could be a new connection from the pool, we should make sure it's in the correct keyspace.
|
|
160
|
+
const keyspace = this._parent.client.keyspace
|
|
161
|
+
if (keyspace && keyspace !== this._connection.keyspace) {
|
|
162
|
+
try {
|
|
163
|
+
await this._connection.changeKeyspace(keyspace)
|
|
164
|
+
} catch (err) {
|
|
165
|
+
// When its a socket error, attempt to retry.
|
|
166
|
+
// Otherwise, rethrow the error to the user.
|
|
167
|
+
return this._handleError(
|
|
168
|
+
RequestExecution._getErrorCode(err),
|
|
169
|
+
err,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this._cancelled) {
|
|
175
|
+
// No need to send the request or invoke any callback
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this._sendOnConnection()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Sends the request using the active connection.
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
_sendOnConnection() {
|
|
187
|
+
this._startTime = process.hrtime()
|
|
188
|
+
|
|
189
|
+
this._operation = this._connection.sendStream(
|
|
190
|
+
this._request,
|
|
191
|
+
this._parent.executionOptions,
|
|
192
|
+
(err, response, length) => {
|
|
193
|
+
const errorCode = RequestExecution._getErrorCode(err)
|
|
194
|
+
|
|
195
|
+
this._trackResponse(
|
|
196
|
+
process.hrtime(this._startTime),
|
|
197
|
+
errorCode,
|
|
198
|
+
err,
|
|
199
|
+
length,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if (this._cancelled) {
|
|
203
|
+
// Avoid handling the response / err
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (errorCode !== errorCodes.none) {
|
|
208
|
+
return this._handleError(errorCode, err)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (response.schemaChange) {
|
|
212
|
+
return promiseUtils.toBackground(
|
|
213
|
+
this._parent.client
|
|
214
|
+
.handleSchemaAgreementAndRefresh(
|
|
215
|
+
this._connection,
|
|
216
|
+
response.schemaChange,
|
|
217
|
+
)
|
|
218
|
+
.then((agreement) => {
|
|
219
|
+
if (this._cancelled) {
|
|
220
|
+
// After the schema agreement method was started, this execution was cancelled
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this._parent.setCompleted(
|
|
225
|
+
null,
|
|
226
|
+
this._getResultSet(response, agreement),
|
|
227
|
+
)
|
|
228
|
+
}),
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (response.keyspaceSet) {
|
|
233
|
+
this._parent.client.keyspace = response.keyspaceSet
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (
|
|
237
|
+
response.meta &&
|
|
238
|
+
response.meta.newResultId &&
|
|
239
|
+
this._request.queryId
|
|
240
|
+
) {
|
|
241
|
+
// Update the resultId on the existing prepared statement.
|
|
242
|
+
// Eventually would want to update the result metadata as well (NODEJS-433)
|
|
243
|
+
const info = this._parent.client.metadata.getPreparedById(
|
|
244
|
+
this._request.queryId,
|
|
245
|
+
)
|
|
246
|
+
info.meta.resultId = response.meta.newResultId
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
this._parent.setCompleted(null, this._getResultSet(response))
|
|
250
|
+
},
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
_trackResponse(latency, errorCode, err, length) {
|
|
255
|
+
// Record metrics
|
|
256
|
+
RequestExecution._invokeMetricsHandler(
|
|
257
|
+
errorCode,
|
|
258
|
+
this._parent.client.metrics,
|
|
259
|
+
err,
|
|
260
|
+
latency,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
// Request tracker
|
|
264
|
+
const tracker = this._parent.client.options.requestTracker
|
|
265
|
+
|
|
266
|
+
if (tracker === null) {
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Avoid using instanceof as property check is faster
|
|
271
|
+
const query = this._request.query || this._request.queries
|
|
272
|
+
const parameters = this._request.params
|
|
273
|
+
const requestLength = this._request.length
|
|
274
|
+
|
|
275
|
+
if (err) {
|
|
276
|
+
tracker.onError(
|
|
277
|
+
this._host,
|
|
278
|
+
query,
|
|
279
|
+
parameters,
|
|
280
|
+
this._parent.executionOptions,
|
|
281
|
+
requestLength,
|
|
282
|
+
err,
|
|
283
|
+
latency,
|
|
284
|
+
)
|
|
285
|
+
} else {
|
|
286
|
+
tracker.onSuccess(
|
|
287
|
+
this._host,
|
|
288
|
+
query,
|
|
289
|
+
parameters,
|
|
290
|
+
this._parent.executionOptions,
|
|
291
|
+
requestLength,
|
|
292
|
+
length,
|
|
293
|
+
latency,
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
_getResultSet(response, agreement) {
|
|
299
|
+
const rs = new types.ResultSet(
|
|
300
|
+
response,
|
|
301
|
+
this._host.address,
|
|
302
|
+
this._parent.triedHosts,
|
|
303
|
+
this._parent.speculativeExecutions,
|
|
304
|
+
this._request.consistency,
|
|
305
|
+
agreement === undefined || agreement,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
if (rs.rawPageState) {
|
|
309
|
+
rs.nextPageAsync = this._parent.getNextPageHandler()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return rs
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Gets the method of the {ClientMetrics} instance depending on the error code and invokes it.
|
|
317
|
+
* @param {Number} errorCode
|
|
318
|
+
* @param {ClientMetrics} metrics
|
|
319
|
+
* @param {Error} err
|
|
320
|
+
* @param {Array} latency
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
static _invokeMetricsHandler(errorCode, metrics, err, latency) {
|
|
324
|
+
const handler = metricsHandlers.get(errorCode)
|
|
325
|
+
if (handler !== undefined) {
|
|
326
|
+
handler(metrics, err, latency)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!err || err instanceof errors.ResponseError) {
|
|
330
|
+
metrics.onResponse(latency)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Gets the method of the {ClientMetrics} instance related to retry depending on the error code and invokes it.
|
|
336
|
+
* @param {Number} errorCode
|
|
337
|
+
* @param {ClientMetrics} metrics
|
|
338
|
+
* @param {Error} err
|
|
339
|
+
* @private
|
|
340
|
+
*/
|
|
341
|
+
static _invokeMetricsHandlerForRetry(errorCode, metrics, err) {
|
|
342
|
+
const handler = metricsRetryHandlers.get(errorCode)
|
|
343
|
+
|
|
344
|
+
if (handler !== undefined) {
|
|
345
|
+
handler(metrics, err)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Allows the handler to cancel the current request.
|
|
351
|
+
* When the request has been already written, we can unset the callback and forget about it.
|
|
352
|
+
*/
|
|
353
|
+
cancel() {
|
|
354
|
+
this._cancelled = true
|
|
355
|
+
|
|
356
|
+
if (this._operation === null) {
|
|
357
|
+
return
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
this._operation.cancel()
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Determines if the current execution was cancelled.
|
|
365
|
+
*/
|
|
366
|
+
wasCancelled() {
|
|
367
|
+
return this._cancelled
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
_handleError(errorCode, err) {
|
|
371
|
+
this._parent.triedHosts[this._host.address] = err
|
|
372
|
+
err["coordinator"] = this._host.address
|
|
373
|
+
|
|
374
|
+
if (errorCode === errorCodes.serverErrorUnprepared) {
|
|
375
|
+
return this._prepareAndRetry(err.queryId)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (
|
|
379
|
+
errorCode === errorCodes.socketError ||
|
|
380
|
+
errorCode === errorCodes.socketErrorBeforeRequestWritten
|
|
381
|
+
) {
|
|
382
|
+
this._host.removeFromPool(this._connection)
|
|
383
|
+
} else if (errorCode === errorCodes.clientTimeout) {
|
|
384
|
+
this._parent.log("warning", err.message)
|
|
385
|
+
this._host.checkHealth(this._connection)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const decisionInfo = this._getDecision(errorCode, err)
|
|
389
|
+
|
|
390
|
+
if (
|
|
391
|
+
!decisionInfo ||
|
|
392
|
+
decisionInfo.decision === retry.RetryPolicy.retryDecision.rethrow
|
|
393
|
+
) {
|
|
394
|
+
if (
|
|
395
|
+
this._request instanceof requests.QueryRequest ||
|
|
396
|
+
this._request instanceof requests.ExecuteRequest
|
|
397
|
+
) {
|
|
398
|
+
err["query"] = this._request.query
|
|
399
|
+
}
|
|
400
|
+
return this._parent.setCompleted(err)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const metrics = this._parent.client.metrics
|
|
404
|
+
|
|
405
|
+
if (decisionInfo.decision === retry.RetryPolicy.retryDecision.ignore) {
|
|
406
|
+
metrics.onIgnoreError(err)
|
|
407
|
+
|
|
408
|
+
// Return an empty ResultSet
|
|
409
|
+
return this._parent.setCompleted(
|
|
410
|
+
null,
|
|
411
|
+
this._getResultSet(utils.emptyObject),
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
RequestExecution._invokeMetricsHandlerForRetry(errorCode, metrics, err)
|
|
416
|
+
|
|
417
|
+
return this._retry(
|
|
418
|
+
decisionInfo.consistency,
|
|
419
|
+
decisionInfo.useCurrentHost,
|
|
420
|
+
)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Gets a decision whether or not to retry based on the error information.
|
|
425
|
+
* @param {Number} errorCode
|
|
426
|
+
* @param {Error} err
|
|
427
|
+
* @returns {{decision, useCurrentHost, consistency}}
|
|
428
|
+
*/
|
|
429
|
+
_getDecision(errorCode, err) {
|
|
430
|
+
const operationInfo = {
|
|
431
|
+
query: this._request && this._request.query,
|
|
432
|
+
executionOptions: this._parent.executionOptions,
|
|
433
|
+
nbRetry: this._retryCount,
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const retryPolicy = operationInfo.executionOptions.getRetryPolicy()
|
|
437
|
+
|
|
438
|
+
switch (errorCode) {
|
|
439
|
+
case errorCodes.socketErrorBeforeRequestWritten:
|
|
440
|
+
// The request was definitely not applied, it's safe to retry.
|
|
441
|
+
// Retry on the current host as there might be other connections open, in case it fails to obtain a connection
|
|
442
|
+
// on the current host, the driver will immediately retry on the next host.
|
|
443
|
+
return retryOnCurrentHost
|
|
444
|
+
case errorCodes.socketError:
|
|
445
|
+
case errorCodes.clientTimeout:
|
|
446
|
+
case errorCodes.serverErrorOverloaded:
|
|
447
|
+
if (operationInfo.executionOptions.isIdempotent()) {
|
|
448
|
+
return retryPolicy.onRequestError(
|
|
449
|
+
operationInfo,
|
|
450
|
+
this._request.consistency,
|
|
451
|
+
err,
|
|
452
|
+
)
|
|
453
|
+
}
|
|
454
|
+
return rethrowDecision
|
|
455
|
+
case errorCodes.serverErrorUnavailable:
|
|
456
|
+
return retryPolicy.onUnavailable(
|
|
457
|
+
operationInfo,
|
|
458
|
+
err.consistencies,
|
|
459
|
+
err.required,
|
|
460
|
+
err.alive,
|
|
461
|
+
)
|
|
462
|
+
case errorCodes.serverErrorReadTimeout:
|
|
463
|
+
return retryPolicy.onReadTimeout(
|
|
464
|
+
operationInfo,
|
|
465
|
+
err.consistencies,
|
|
466
|
+
err.received,
|
|
467
|
+
err.blockFor,
|
|
468
|
+
err.isDataPresent,
|
|
469
|
+
)
|
|
470
|
+
case errorCodes.serverErrorWriteTimeout:
|
|
471
|
+
if (operationInfo.executionOptions.isIdempotent()) {
|
|
472
|
+
return retryPolicy.onWriteTimeout(
|
|
473
|
+
operationInfo,
|
|
474
|
+
err.consistencies,
|
|
475
|
+
err.received,
|
|
476
|
+
err.blockFor,
|
|
477
|
+
err.writeType,
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
return rethrowDecision
|
|
481
|
+
default:
|
|
482
|
+
return rethrowDecision
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
static _getErrorCode(err) {
|
|
487
|
+
if (!err) {
|
|
488
|
+
return errorCodes.none
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (err.isSocketError) {
|
|
492
|
+
if (err.requestNotWritten) {
|
|
493
|
+
return errorCodes.socketErrorBeforeRequestWritten
|
|
494
|
+
}
|
|
495
|
+
return errorCodes.socketError
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (err instanceof errors.OperationTimedOutError) {
|
|
499
|
+
return errorCodes.clientTimeout
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (err instanceof errors.ResponseError) {
|
|
503
|
+
switch (err.code) {
|
|
504
|
+
case types.responseErrorCodes.overloaded:
|
|
505
|
+
case types.responseErrorCodes.isBootstrapping:
|
|
506
|
+
case types.responseErrorCodes.truncateError:
|
|
507
|
+
return errorCodes.serverErrorOverloaded
|
|
508
|
+
case types.responseErrorCodes.unavailableException:
|
|
509
|
+
return errorCodes.serverErrorUnavailable
|
|
510
|
+
case types.responseErrorCodes.readTimeout:
|
|
511
|
+
return errorCodes.serverErrorReadTimeout
|
|
512
|
+
case types.responseErrorCodes.writeTimeout:
|
|
513
|
+
return errorCodes.serverErrorWriteTimeout
|
|
514
|
+
case types.responseErrorCodes.unprepared:
|
|
515
|
+
return errorCodes.serverErrorUnprepared
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return errorCodes.serverErrorOther
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* @param {Number|undefined} consistency
|
|
524
|
+
* @param {Boolean} useCurrentHost
|
|
525
|
+
* @param {Object} [meta]
|
|
526
|
+
* @private
|
|
527
|
+
*/
|
|
528
|
+
_retry(consistency, useCurrentHost, meta) {
|
|
529
|
+
if (this._cancelled) {
|
|
530
|
+
// No point in retrying
|
|
531
|
+
return
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
this._parent.log("info", "Retrying request")
|
|
535
|
+
this._retryCount++
|
|
536
|
+
|
|
537
|
+
if (
|
|
538
|
+
meta ||
|
|
539
|
+
(typeof consistency === "number" &&
|
|
540
|
+
this._request.consistency !== consistency)
|
|
541
|
+
) {
|
|
542
|
+
this._request = this._request.clone()
|
|
543
|
+
if (typeof consistency === "number") {
|
|
544
|
+
this._request.consistency = consistency
|
|
545
|
+
}
|
|
546
|
+
// possible that we are retrying because we had to reprepare. In this case it is also possible
|
|
547
|
+
// that our known metadata had changed, therefore we update it on the request.
|
|
548
|
+
if (meta) {
|
|
549
|
+
this._request.meta = meta
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (useCurrentHost !== false) {
|
|
554
|
+
// Reusing the existing connection is suitable for the most common scenarios, like server read timeouts that
|
|
555
|
+
// will be fixed with a new request.
|
|
556
|
+
// To cover all scenarios (e.g., where a different connection to the same host might mean something different),
|
|
557
|
+
// we obtain a new connection from the host pool.
|
|
558
|
+
// When there was a socket error, the connection provided was already removed from the pool earlier.
|
|
559
|
+
try {
|
|
560
|
+
this._connection = this._host.borrowConnection(this._connection)
|
|
561
|
+
} catch (err) {
|
|
562
|
+
// All connections are busy (`BusyConnectionError`) or there isn't a ready connection in the pool (`Error`)
|
|
563
|
+
// The retry policy declared the intention to retry on the current host but its not available anymore.
|
|
564
|
+
// Use the next host
|
|
565
|
+
return promiseUtils.toBackground(this.restart())
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return this._sendOnConnection()
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Use the next host in the query plan to send the request in the background
|
|
572
|
+
promiseUtils.toBackground(this.restart())
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Issues a PREPARE request on the current connection.
|
|
577
|
+
* If there's a socket or timeout issue, it moves to next host and executes the original request.
|
|
578
|
+
* @param {Buffer} queryId
|
|
579
|
+
* @private
|
|
580
|
+
*/
|
|
581
|
+
_prepareAndRetry(queryId) {
|
|
582
|
+
const connection = this._connection
|
|
583
|
+
|
|
584
|
+
this._parent.log(
|
|
585
|
+
"info",
|
|
586
|
+
`Query 0x${queryId.toString("hex")} not prepared on` +
|
|
587
|
+
` host ${connection.endpointFriendlyName}, preparing and retrying`,
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
const info = this._parent.client.metadata.getPreparedById(queryId)
|
|
591
|
+
|
|
592
|
+
if (!info) {
|
|
593
|
+
return this._parent.setCompleted(
|
|
594
|
+
new errors.DriverInternalError(
|
|
595
|
+
`Unprepared response invalid, id: 0x${queryId.toString("hex")}`,
|
|
596
|
+
),
|
|
597
|
+
)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const version = this._connection.protocolVersion
|
|
601
|
+
|
|
602
|
+
if (
|
|
603
|
+
!types.protocolVersion.supportsKeyspaceInRequest(version) &&
|
|
604
|
+
info.keyspace &&
|
|
605
|
+
info.keyspace !== connection.keyspace
|
|
606
|
+
) {
|
|
607
|
+
return this._parent.setCompleted(
|
|
608
|
+
new Error(
|
|
609
|
+
`Query was prepared on keyspace ${info.keyspace}, can't execute it on ${connection.keyspace} (${info.query})`,
|
|
610
|
+
),
|
|
611
|
+
)
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const self = this
|
|
615
|
+
this._connection.prepareOnce(
|
|
616
|
+
info.query,
|
|
617
|
+
info.keyspace,
|
|
618
|
+
function (err, result) {
|
|
619
|
+
if (err) {
|
|
620
|
+
if (
|
|
621
|
+
!err.isSocketError &&
|
|
622
|
+
err instanceof errors.OperationTimedOutError
|
|
623
|
+
) {
|
|
624
|
+
self._parent.log(
|
|
625
|
+
"warning",
|
|
626
|
+
`Unexpected timeout error when re-preparing query on host ${connection.endpointFriendlyName}`,
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// There was a failure re-preparing on this connection.
|
|
631
|
+
// Execute the original request on the next connection and forget about the PREPARE-UNPREPARE flow.
|
|
632
|
+
return self._retry(undefined, false)
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// It's possible that when re-preparing we got new metadata (i.e. if schema changed), update cache.
|
|
636
|
+
info.meta = result.meta
|
|
637
|
+
// pass the metadata so it can be used in retry.
|
|
638
|
+
self._retry(undefined, true, result.meta)
|
|
639
|
+
},
|
|
640
|
+
)
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
export default RequestExecution
|