@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.
Files changed (153) hide show
  1. package/batch/index.d.ts +3 -3
  2. package/batch/index.d.ts.map +1 -1
  3. package/client.d.ts +6 -5
  4. package/client.d.ts.map +1 -1
  5. package/client.js +7 -7
  6. package/client.js.map +1 -1
  7. package/cql_gen/create_table.d.ts +1 -1
  8. package/cql_gen/create_table.d.ts.map +1 -1
  9. package/document/index.d.ts +3 -3
  10. package/document/index.d.ts.map +1 -1
  11. package/driver/LICENSE.txt +177 -0
  12. package/driver/NOTICE.txt +67 -0
  13. package/driver/auth/index.d.ts +37 -0
  14. package/driver/auth/index.js +37 -0
  15. package/driver/auth/no-auth-provider.js +73 -0
  16. package/driver/auth/plain-text-auth-provider.js +81 -0
  17. package/driver/auth/provider.js +77 -0
  18. package/driver/client-options.js +442 -0
  19. package/driver/client.js +1267 -0
  20. package/driver/concurrent/index.d.ts +49 -0
  21. package/driver/concurrent/index.js +366 -0
  22. package/driver/connection.js +1034 -0
  23. package/driver/control-connection.js +1282 -0
  24. package/driver/encoder.js +2316 -0
  25. package/driver/errors.js +223 -0
  26. package/driver/execution-options.js +612 -0
  27. package/driver/execution-profile.js +274 -0
  28. package/driver/host-connection-pool.js +587 -0
  29. package/driver/host.js +699 -0
  30. package/driver/index.d.ts +387 -0
  31. package/driver/index.js +81 -0
  32. package/driver/mapping/cache.js +214 -0
  33. package/driver/mapping/doc-info-adapter.js +171 -0
  34. package/driver/mapping/index.d.ts +219 -0
  35. package/driver/mapping/index.js +57 -0
  36. package/driver/mapping/mapper.js +225 -0
  37. package/driver/mapping/mapping-handler.js +641 -0
  38. package/driver/mapping/model-batch-item.js +215 -0
  39. package/driver/mapping/model-batch-mapper.js +141 -0
  40. package/driver/mapping/model-mapper.js +315 -0
  41. package/driver/mapping/model-mapping-info.js +225 -0
  42. package/driver/mapping/object-selector.js +417 -0
  43. package/driver/mapping/q.js +156 -0
  44. package/driver/mapping/query-generator.js +556 -0
  45. package/driver/mapping/result-mapper.js +123 -0
  46. package/driver/mapping/result.js +139 -0
  47. package/driver/mapping/table-mappings.js +133 -0
  48. package/driver/mapping/tree.js +160 -0
  49. package/driver/metadata/aggregate.js +79 -0
  50. package/driver/metadata/client-state.js +119 -0
  51. package/driver/metadata/data-collection.js +182 -0
  52. package/driver/metadata/event-debouncer.js +174 -0
  53. package/driver/metadata/index.d.ts +276 -0
  54. package/driver/metadata/index.js +1156 -0
  55. package/driver/metadata/materialized-view.js +49 -0
  56. package/driver/metadata/schema-function.js +98 -0
  57. package/driver/metadata/schema-index.js +166 -0
  58. package/driver/metadata/schema-parser.js +1399 -0
  59. package/driver/metadata/table-metadata.js +77 -0
  60. package/driver/operation-state.js +206 -0
  61. package/driver/policies/address-resolution.js +145 -0
  62. package/driver/policies/index.d.ts +241 -0
  63. package/driver/policies/index.js +110 -0
  64. package/driver/policies/load-balancing.js +970 -0
  65. package/driver/policies/reconnection.js +166 -0
  66. package/driver/policies/retry.js +326 -0
  67. package/driver/policies/speculative-execution.js +150 -0
  68. package/driver/policies/timestamp-generation.js +176 -0
  69. package/driver/prepare-handler.js +347 -0
  70. package/driver/promise-utils.js +191 -0
  71. package/driver/readers.js +624 -0
  72. package/driver/request-execution.js +644 -0
  73. package/driver/request-handler.js +332 -0
  74. package/driver/requests.js +618 -0
  75. package/driver/stream-id-stack.js +209 -0
  76. package/driver/streams.js +745 -0
  77. package/driver/token.js +325 -0
  78. package/driver/tokenizer.js +631 -0
  79. package/driver/types/big-decimal.js +282 -0
  80. package/driver/types/duration.js +576 -0
  81. package/driver/types/index.d.ts +486 -0
  82. package/driver/types/index.js +733 -0
  83. package/driver/types/inet-address.js +262 -0
  84. package/driver/types/integer.js +818 -0
  85. package/driver/types/local-date.js +280 -0
  86. package/driver/types/local-time.js +299 -0
  87. package/driver/types/mutable-long.js +385 -0
  88. package/driver/types/protocol-version.js +391 -0
  89. package/driver/types/result-set.js +287 -0
  90. package/driver/types/result-stream.js +164 -0
  91. package/driver/types/row.js +85 -0
  92. package/driver/types/time-uuid.js +414 -0
  93. package/driver/types/tuple.js +103 -0
  94. package/driver/types/uuid.js +160 -0
  95. package/driver/types/vector.js +130 -0
  96. package/driver/types/version-number.js +153 -0
  97. package/driver/utils.js +1485 -0
  98. package/driver/writers.js +350 -0
  99. package/global.d.ts +1 -1
  100. package/global.d.ts.map +1 -1
  101. package/index.d.ts +6 -6
  102. package/index.d.ts.map +1 -1
  103. package/index.js +6 -6
  104. package/index.js.map +1 -1
  105. package/migrate/index.d.ts +1 -1
  106. package/migrate/index.d.ts.map +1 -1
  107. package/migrate/index.js +1 -1
  108. package/migrate/index.js.map +1 -1
  109. package/model/index.d.ts +6 -6
  110. package/model/index.d.ts.map +1 -1
  111. package/model/index.js +10 -10
  112. package/model/index.js.map +1 -1
  113. package/operations/countAll.d.ts +1 -1
  114. package/operations/countAll.d.ts.map +1 -1
  115. package/operations/delete.d.ts +3 -4
  116. package/operations/delete.d.ts.map +1 -1
  117. package/operations/delete.js +1 -1
  118. package/operations/delete.js.map +1 -1
  119. package/operations/find.d.ts +2 -2
  120. package/operations/find.d.ts.map +1 -1
  121. package/operations/find.js +1 -1
  122. package/operations/find.js.map +1 -1
  123. package/operations/findOne.d.ts +2 -2
  124. package/operations/findOne.d.ts.map +1 -1
  125. package/operations/findOne.js +1 -1
  126. package/operations/findOne.js.map +1 -1
  127. package/operations/insert.d.ts +3 -3
  128. package/operations/insert.d.ts.map +1 -1
  129. package/operations/insert.js +2 -2
  130. package/operations/insert.js.map +1 -1
  131. package/operations/sync.d.ts +1 -1
  132. package/operations/sync.d.ts.map +1 -1
  133. package/operations/sync.js +1 -1
  134. package/operations/sync.js.map +1 -1
  135. package/operations/tableExists.d.ts +1 -1
  136. package/operations/tableExists.d.ts.map +1 -1
  137. package/operations/update.d.ts +3 -3
  138. package/operations/update.d.ts.map +1 -1
  139. package/operations/update.js +2 -2
  140. package/operations/update.js.map +1 -1
  141. package/package.json +4 -12
  142. package/schema/index.d.ts +1 -1
  143. package/schema/index.d.ts.map +1 -1
  144. package/types.d.ts +4 -4
  145. package/types.d.ts.map +1 -1
  146. package/utils/queryParser.d.ts +1 -1
  147. package/utils/queryParser.d.ts.map +1 -1
  148. package/utils/queryParser.js +1 -1
  149. package/utils/queryParser.js.map +1 -1
  150. package/utils/typeChecker.d.ts +1 -1
  151. package/utils/typeChecker.d.ts.map +1 -1
  152. package/utils/typeChecker.js +1 -1
  153. 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