@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,970 @@
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 util from "util"
19
+ import types from "../types/index.js"
20
+ import utils from "../utils.js"
21
+ import errors from "../errors.js"
22
+
23
+ const doneIteratorObject = Object.freeze({ done: true })
24
+ const newlyUpInterval = 60000
25
+
26
+ /** @module policies/loadBalancing */
27
+ /**
28
+ * Base class for Load Balancing Policies
29
+ * @constructor
30
+ */
31
+ function LoadBalancingPolicy() {}
32
+
33
+ /**
34
+ * Initializes the load balancing policy, called after the driver obtained the information of the cluster.
35
+ * @param {Client} client
36
+ * @param {HostMap} hosts
37
+ * @param {Function} callback
38
+ */
39
+ LoadBalancingPolicy.prototype.init = function (client, hosts, callback) {
40
+ this.client = client
41
+ this.hosts = hosts
42
+ callback()
43
+ }
44
+
45
+ /**
46
+ * Returns the distance assigned by this policy to the provided host.
47
+ * @param {Host} host
48
+ */
49
+ LoadBalancingPolicy.prototype.getDistance = function (host) {
50
+ return types.distance.local
51
+ }
52
+
53
+ /**
54
+ * Returns an iterator with the hosts for a new query.
55
+ * Each new query will call this method. The first host in the result will
56
+ * then be used to perform the query.
57
+ * @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
58
+ * @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
59
+ * @param {Function} callback The function to be invoked with the error as first parameter and the host iterator as
60
+ * second parameter.
61
+ */
62
+ LoadBalancingPolicy.prototype.newQueryPlan = function (
63
+ keyspace,
64
+ executionOptions,
65
+ callback,
66
+ ) {
67
+ callback(
68
+ new Error(
69
+ "You must implement a query plan for the LoadBalancingPolicy class",
70
+ ),
71
+ )
72
+ }
73
+
74
+ /**
75
+ * Gets an associative array containing the policy options.
76
+ */
77
+ LoadBalancingPolicy.prototype.getOptions = function () {
78
+ return new Map()
79
+ }
80
+
81
+ /**
82
+ * This policy yield nodes in a round-robin fashion.
83
+ * @extends LoadBalancingPolicy
84
+ * @constructor
85
+ */
86
+ function RoundRobinPolicy() {
87
+ this.index = 0
88
+ }
89
+
90
+ util.inherits(RoundRobinPolicy, LoadBalancingPolicy)
91
+
92
+ /**
93
+ * Returns an iterator with the hosts to be used as coordinator for a query.
94
+ * @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
95
+ * @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
96
+ * @param {Function} callback The function to be invoked with the error as first parameter and the host iterator as
97
+ * second parameter.
98
+ */
99
+ RoundRobinPolicy.prototype.newQueryPlan = function (
100
+ keyspace,
101
+ executionOptions,
102
+ callback,
103
+ ) {
104
+ if (!this.hosts) {
105
+ return callback(new Error("Load balancing policy not initialized"))
106
+ }
107
+ const hosts = this.hosts.values()
108
+ const self = this
109
+ let counter = 0
110
+
111
+ let planIndex = self.index % hosts.length
112
+ self.index += 1
113
+ if (self.index >= utils.maxInt) {
114
+ self.index = 0
115
+ }
116
+
117
+ callback(null, {
118
+ next: function () {
119
+ if (++counter > hosts.length) {
120
+ return doneIteratorObject
121
+ }
122
+ return { value: hosts[planIndex++ % hosts.length], done: false }
123
+ },
124
+ })
125
+ }
126
+
127
+ /**
128
+ * A data-center aware Round-robin load balancing policy.
129
+ * This policy provides round-robin queries over the nodes of the local
130
+ * data center.
131
+ * @param {?String} [localDc] local datacenter name. This value overrides the 'localDataCenter' Client option \
132
+ * and is useful for cases where you have multiple execution profiles that you intend on using for routing
133
+ * requests to different data centers.
134
+ * @extends {LoadBalancingPolicy}
135
+ * @constructor
136
+ */
137
+ function DCAwareRoundRobinPolicy(localDc) {
138
+ this.localDc = localDc
139
+ this.index = 0
140
+ /** @type {Array} */
141
+ this.localHostsArray = null
142
+ }
143
+
144
+ util.inherits(DCAwareRoundRobinPolicy, LoadBalancingPolicy)
145
+
146
+ /**
147
+ * Initializes the load balancing policy.
148
+ * @param {Client} client
149
+ * @param {HostMap} hosts
150
+ * @param {Function} callback
151
+ */
152
+ DCAwareRoundRobinPolicy.prototype.init = function (client, hosts, callback) {
153
+ this.client = client
154
+ this.hosts = hosts
155
+ hosts.on("add", this._cleanHostCache.bind(this))
156
+ hosts.on("remove", this._cleanHostCache.bind(this))
157
+
158
+ try {
159
+ setLocalDc(this, client, this.hosts)
160
+ } catch (err) {
161
+ return callback(err)
162
+ }
163
+
164
+ callback()
165
+ }
166
+
167
+ /**
168
+ * Returns the distance depending on the datacenter.
169
+ * @param {Host} host
170
+ */
171
+ DCAwareRoundRobinPolicy.prototype.getDistance = function (host) {
172
+ if (host.datacenter === this.localDc) {
173
+ return types.distance.local
174
+ }
175
+
176
+ return types.distance.ignored
177
+ }
178
+
179
+ DCAwareRoundRobinPolicy.prototype._cleanHostCache = function () {
180
+ this.localHostsArray = null
181
+ }
182
+
183
+ DCAwareRoundRobinPolicy.prototype._resolveLocalHosts = function () {
184
+ const hosts = this.hosts.values()
185
+ if (this.localHostsArray) {
186
+ //there were already calculated
187
+ return
188
+ }
189
+ this.localHostsArray = []
190
+ hosts.forEach(function (h) {
191
+ if (!h.datacenter) {
192
+ //not a remote dc node
193
+ return
194
+ }
195
+ if (h.datacenter === this.localDc) {
196
+ this.localHostsArray.push(h)
197
+ }
198
+ }, this)
199
+ }
200
+
201
+ /**
202
+ * It returns an iterator that yields local nodes.
203
+ * @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
204
+ * @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
205
+ * @param {Function} callback The function to be invoked with the error as first parameter and the host iterator as
206
+ * second parameter.
207
+ */
208
+ DCAwareRoundRobinPolicy.prototype.newQueryPlan = function (
209
+ keyspace,
210
+ executionOptions,
211
+ callback,
212
+ ) {
213
+ if (!this.hosts) {
214
+ return callback(new Error("Load balancing policy not initialized"))
215
+ }
216
+ this.index += 1
217
+ if (this.index >= utils.maxInt) {
218
+ this.index = 0
219
+ }
220
+ this._resolveLocalHosts()
221
+ // Use a local reference of hosts
222
+ const localHostsArray = this.localHostsArray
223
+ let planLocalIndex = this.index
224
+ let counter = 0
225
+ callback(null, {
226
+ next: function () {
227
+ let host
228
+ if (counter++ < localHostsArray.length) {
229
+ host =
230
+ localHostsArray[planLocalIndex++ % localHostsArray.length]
231
+ return { value: host, done: false }
232
+ }
233
+ return doneIteratorObject
234
+ },
235
+ })
236
+ }
237
+
238
+ /**
239
+ * Gets an associative array containing the policy options.
240
+ */
241
+ DCAwareRoundRobinPolicy.prototype.getOptions = function () {
242
+ return new Map([["localDataCenter", this.localDc]])
243
+ }
244
+
245
+ /**
246
+ * A wrapper load balancing policy that add token awareness to a child policy.
247
+ * @param {LoadBalancingPolicy} childPolicy
248
+ * @extends LoadBalancingPolicy
249
+ * @constructor
250
+ */
251
+ function TokenAwarePolicy(childPolicy) {
252
+ if (!childPolicy) {
253
+ throw new Error("You must specify a child load balancing policy")
254
+ }
255
+ this.childPolicy = childPolicy
256
+ }
257
+
258
+ util.inherits(TokenAwarePolicy, LoadBalancingPolicy)
259
+
260
+ TokenAwarePolicy.prototype.init = function (client, hosts, callback) {
261
+ this.client = client
262
+ this.hosts = hosts
263
+ this.childPolicy.init(client, hosts, callback)
264
+ }
265
+
266
+ TokenAwarePolicy.prototype.getDistance = function (host) {
267
+ return this.childPolicy.getDistance(host)
268
+ }
269
+
270
+ /**
271
+ * Returns the hosts to use for a new query.
272
+ * The returned plan will return local replicas first, if replicas can be determined, followed by the plan of the
273
+ * child policy.
274
+ * @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
275
+ * @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
276
+ * @param {Function} callback The function to be invoked with the error as first parameter and the host iterator as
277
+ * second parameter.
278
+ */
279
+ TokenAwarePolicy.prototype.newQueryPlan = function (
280
+ keyspace,
281
+ executionOptions,
282
+ callback,
283
+ ) {
284
+ let routingKey
285
+ if (executionOptions) {
286
+ routingKey = executionOptions.getRoutingKey()
287
+ if (executionOptions.getKeyspace()) {
288
+ keyspace = executionOptions.getKeyspace()
289
+ }
290
+ }
291
+ let replicas
292
+ if (routingKey) {
293
+ replicas = this.client.getReplicas(keyspace, routingKey)
294
+ }
295
+ if (!routingKey || !replicas) {
296
+ return this.childPolicy.newQueryPlan(
297
+ keyspace,
298
+ executionOptions,
299
+ callback,
300
+ )
301
+ }
302
+ const iterator = new TokenAwareIterator(
303
+ keyspace,
304
+ executionOptions,
305
+ replicas,
306
+ this.childPolicy,
307
+ )
308
+ iterator.iterate(callback)
309
+ }
310
+
311
+ /**
312
+ * An iterator that holds the context for the subsequent next() calls
313
+ * @param {String} keyspace
314
+ * @param {ExecutionOptions} execOptions
315
+ * @param {Array} replicas
316
+ * @param childPolicy
317
+ * @constructor
318
+ * @ignore
319
+ */
320
+ function TokenAwareIterator(keyspace, execOptions, replicas, childPolicy) {
321
+ this.keyspace = keyspace
322
+ this.childPolicy = childPolicy
323
+ this.options = execOptions
324
+ this.localReplicas = []
325
+ this.replicaIndex = 0
326
+ this.replicaMap = {}
327
+ this.childIterator = null
328
+ // Memoize the local replicas
329
+ // The amount of local replicas should be defined before start iterating, in order to select an
330
+ // appropriate (pseudo random) startIndex
331
+ for (let i = 0; i < replicas.length; i++) {
332
+ const host = replicas[i]
333
+ if (this.childPolicy.getDistance(host) !== types.distance.local) {
334
+ continue
335
+ }
336
+ this.replicaMap[host.address] = true
337
+ this.localReplicas.push(host)
338
+ }
339
+ // We use a PRNG to set the replica index
340
+ // We only care about proportional fair scheduling between replicas of a given token
341
+ // Math.random() has an extremely short permutation cycle length but we don't care about collisions
342
+ this.startIndex = Math.floor(Math.random() * this.localReplicas.length)
343
+ }
344
+
345
+ TokenAwareIterator.prototype.iterate = function (callback) {
346
+ //Load the child policy hosts
347
+ const self = this
348
+ this.childPolicy.newQueryPlan(
349
+ this.keyspace,
350
+ this.options,
351
+ function (err, iterator) {
352
+ if (err) {
353
+ return callback(err)
354
+ }
355
+ //get the iterator of the child policy in case is needed
356
+ self.childIterator = iterator
357
+ callback(null, {
358
+ next: function () {
359
+ return self.computeNext()
360
+ },
361
+ })
362
+ },
363
+ )
364
+ }
365
+
366
+ TokenAwareIterator.prototype.computeNext = function () {
367
+ let host
368
+ if (this.replicaIndex < this.localReplicas.length) {
369
+ host =
370
+ this.localReplicas[
371
+ (this.startIndex + this.replicaIndex++) %
372
+ this.localReplicas.length
373
+ ]
374
+ return { value: host, done: false }
375
+ }
376
+ // Return hosts from child policy
377
+ let item
378
+ while ((item = this.childIterator.next()) && !item.done) {
379
+ if (this.replicaMap[item.value.address]) {
380
+ // Avoid yielding local replicas from the child load balancing policy query plan
381
+ continue
382
+ }
383
+ return item
384
+ }
385
+ return doneIteratorObject
386
+ }
387
+
388
+ /**
389
+ * Gets an associative array containing the policy options.
390
+ */
391
+ TokenAwarePolicy.prototype.getOptions = function () {
392
+ const map = new Map([
393
+ [
394
+ "childPolicy",
395
+ this.childPolicy.constructor !== undefined
396
+ ? this.childPolicy.constructor.name
397
+ : null,
398
+ ],
399
+ ])
400
+
401
+ if (this.childPolicy instanceof DCAwareRoundRobinPolicy) {
402
+ map.set("localDataCenter", this.childPolicy.localDc)
403
+ }
404
+
405
+ return map
406
+ }
407
+
408
+ /**
409
+ * Create a new policy that wraps the provided child policy but only "allow" hosts
410
+ * from the provided list.
411
+ * @class
412
+ * @classdesc
413
+ * A load balancing policy wrapper that ensure that only hosts from a provided
414
+ * allow list will ever be returned.
415
+ * <p>
416
+ * This policy wraps another load balancing policy and will delegate the choice
417
+ * of hosts to the wrapped policy with the exception that only hosts contained
418
+ * in the allow list provided when constructing this policy will ever be
419
+ * returned. Any host not in the while list will be considered ignored
420
+ * and thus will not be connected to.
421
+ * <p>
422
+ * This policy can be useful to ensure that the driver only connects to a
423
+ * predefined set of hosts. Keep in mind however that this policy defeats
424
+ * somewhat the host auto-detection of the driver. As such, this policy is only
425
+ * useful in a few special cases or for testing, but is not optimal in general.
426
+ * If all you want to do is limiting connections to hosts of the local
427
+ * data-center then you should use DCAwareRoundRobinPolicy and *not* this policy
428
+ * in particular.
429
+ * @param {LoadBalancingPolicy} childPolicy the wrapped policy.
430
+ * @param {Array.<string>} allowList The hosts address in the format ipAddress:port.
431
+ * Only hosts from this list may get connected
432
+ * to (whether they will get connected to or not depends on the child policy).
433
+ * @extends LoadBalancingPolicy
434
+ * @constructor
435
+ */
436
+ function AllowListPolicy(childPolicy, allowList) {
437
+ if (!childPolicy) {
438
+ throw new Error("You must specify a child load balancing policy")
439
+ }
440
+ if (!Array.isArray(allowList)) {
441
+ throw new Error("You must provide the list of allowed host addresses")
442
+ }
443
+
444
+ this.childPolicy = childPolicy
445
+ this.allowList = new Map(allowList.map((address) => [address, true]))
446
+ }
447
+
448
+ util.inherits(AllowListPolicy, LoadBalancingPolicy)
449
+
450
+ AllowListPolicy.prototype.init = function (client, hosts, callback) {
451
+ this.childPolicy.init(client, hosts, callback)
452
+ }
453
+
454
+ /**
455
+ * Uses the child policy to return the distance to the host if included in the allow list.
456
+ * Any host not in the while list will be considered ignored.
457
+ * @param host
458
+ */
459
+ AllowListPolicy.prototype.getDistance = function (host) {
460
+ if (!this._contains(host)) {
461
+ return types.distance.ignored
462
+ }
463
+ return this.childPolicy.getDistance(host)
464
+ }
465
+
466
+ /**
467
+ * @param {Host} host
468
+ * @returns {boolean}
469
+ * @private
470
+ */
471
+ AllowListPolicy.prototype._contains = function (host) {
472
+ return !!this.allowList.get(host.address)
473
+ }
474
+
475
+ /**
476
+ * Returns the hosts to use for a new query filtered by the allow list.
477
+ */
478
+ AllowListPolicy.prototype.newQueryPlan = function (keyspace, info, callback) {
479
+ const self = this
480
+ this.childPolicy.newQueryPlan(keyspace, info, function (err, iterator) {
481
+ if (err) {
482
+ return callback(err)
483
+ }
484
+ callback(null, self._filter(iterator))
485
+ })
486
+ }
487
+
488
+ AllowListPolicy.prototype._filter = function (childIterator) {
489
+ const self = this
490
+ return {
491
+ next: function () {
492
+ const item = childIterator.next()
493
+ if (!item.done && !self._contains(item.value)) {
494
+ return this.next()
495
+ }
496
+ return item
497
+ },
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Gets an associative array containing the policy options.
503
+ */
504
+ AllowListPolicy.prototype.getOptions = function () {
505
+ return new Map([
506
+ [
507
+ "childPolicy",
508
+ this.childPolicy.constructor !== undefined
509
+ ? this.childPolicy.constructor.name
510
+ : null,
511
+ ],
512
+ ["allowList", Array.from(this.allowList.keys())],
513
+ ])
514
+ }
515
+
516
+ /**
517
+ * Creates a new instance of the policy.
518
+ * @classdesc
519
+ * Exposed for backward-compatibility only, it's recommended that you use {@link AllowListPolicy} instead.
520
+ * @param {LoadBalancingPolicy} childPolicy the wrapped policy.
521
+ * @param {Array.<string>} allowList The hosts address in the format ipAddress:port.
522
+ * Only hosts from this list may get connected to (whether they will get connected to or not depends on the child
523
+ * policy).
524
+ * @extends AllowListPolicy
525
+ * @deprecated Use allow-list instead. It will be removed in future major versions.
526
+ * @constructor
527
+ */
528
+ function WhiteListPolicy(childPolicy, allowList) {
529
+ AllowListPolicy.call(this, childPolicy, allowList)
530
+ }
531
+
532
+ util.inherits(WhiteListPolicy, AllowListPolicy)
533
+
534
+ /**
535
+ * A load-balancing policy implementation that attempts to fairly distribute the load based on the amount of in-flight
536
+ * request per hosts. The local replicas are initially shuffled and
537
+ * <a href="https://www.eecs.harvard.edu/~michaelm/postscripts/mythesis.pdf">between the first two nodes in the
538
+ * shuffled list, the one with fewer in-flight requests is selected as coordinator</a>.
539
+ *
540
+ * <p>
541
+ * Additionally, it detects unresponsive replicas and reorders them at the back of the query plan.
542
+ * </p>
543
+ *
544
+ * <p>
545
+ * For graph analytics queries, it uses the preferred analytics graph server previously obtained by driver as first
546
+ * host in the query plan.
547
+ * </p>
548
+ */
549
+ class DefaultLoadBalancingPolicy extends LoadBalancingPolicy {
550
+ /**
551
+ * Creates a new instance of <code>DefaultLoadBalancingPolicy</code>.
552
+ * @param {String|Object} [options] The local data center name or the optional policy options object.
553
+ * <p>
554
+ * Note that when providing the local data center name, it overrides <code>localDataCenter</code> option at
555
+ * <code>Client</code> level.
556
+ * </p>
557
+ * @param {String} [options.localDc] local data center name. This value overrides the 'localDataCenter' Client option
558
+ * and is useful for cases where you have multiple execution profiles that you intend on using for routing
559
+ * requests to different data centers.
560
+ * @param {Function} [options.filter] A function to apply to determine if hosts are included in the query plan.
561
+ * The function takes a Host parameter and returns a Boolean.
562
+ */
563
+ constructor(options) {
564
+ super()
565
+
566
+ if (typeof options === "string") {
567
+ options = { localDc: options }
568
+ } else if (!options) {
569
+ options = utils.emptyObject
570
+ }
571
+
572
+ this._client = null
573
+ this._hosts = null
574
+ this._filteredHosts = null
575
+ this._preferredHost = null
576
+ this._index = 0
577
+ this.localDc = options.localDc
578
+ this._filter = options.filter || this._defaultFilter
579
+
580
+ // Allow some checks to be injected
581
+ if (options.isHostNewlyUp) {
582
+ this._isHostNewlyUp = options.isHostNewlyUp
583
+ }
584
+ if (options.healthCheck) {
585
+ this._healthCheck = options.healthCheck
586
+ }
587
+ if (options.compare) {
588
+ this._compare = options.compare
589
+ }
590
+ if (options.getReplicas) {
591
+ this._getReplicas = options.getReplicas
592
+ }
593
+ }
594
+
595
+ /**
596
+ * Initializes the load balancing policy, called after the driver obtained the information of the cluster.
597
+ * @param {Client} client
598
+ * @param {HostMap} hosts
599
+ * @param {Function} callback
600
+ */
601
+ init(client, hosts, callback) {
602
+ this._client = client
603
+ this._hosts = hosts
604
+
605
+ // Clean local host cache
606
+ this._hosts.on("add", () => (this._filteredHosts = null))
607
+ this._hosts.on("remove", () => (this._filteredHosts = null))
608
+
609
+ try {
610
+ setLocalDc(this, client, this._hosts)
611
+ } catch (err) {
612
+ return callback(err)
613
+ }
614
+
615
+ callback()
616
+ }
617
+
618
+ /**
619
+ * Returns the distance assigned by this policy to the provided host, relatively to the client instance.
620
+ * @param {Host} host
621
+ */
622
+ getDistance(host) {
623
+ if (this._preferredHost !== null && host === this._preferredHost) {
624
+ // Set the last preferred host as local.
625
+ // It ensures that the pool for the graph analytics host has the appropriate size
626
+ return types.distance.local
627
+ }
628
+
629
+ if (!this._filter(host)) {
630
+ return types.distance.ignored
631
+ }
632
+
633
+ return host.datacenter === this.localDc
634
+ ? types.distance.local
635
+ : types.distance.ignored
636
+ }
637
+
638
+ /**
639
+ * Returns a host iterator to be used for a query execution.
640
+ * @override
641
+ * @param {String} keyspace
642
+ * @param {ExecutionOptions} executionOptions
643
+ * @param {Function} callback
644
+ */
645
+ newQueryPlan(keyspace, executionOptions, callback) {
646
+ let routingKey
647
+ let preferredHost
648
+
649
+ if (executionOptions) {
650
+ routingKey = executionOptions.getRoutingKey()
651
+
652
+ if (executionOptions.getKeyspace()) {
653
+ keyspace = executionOptions.getKeyspace()
654
+ }
655
+
656
+ preferredHost = executionOptions.getPreferredHost()
657
+ }
658
+
659
+ let iterable
660
+
661
+ if (!keyspace || !routingKey) {
662
+ iterable = this._getLocalHosts()
663
+ } else {
664
+ iterable = this._getReplicasAndLocalHosts(keyspace, routingKey)
665
+ }
666
+
667
+ if (preferredHost) {
668
+ // Set it on an instance level field to set the distance
669
+ this._preferredHost = preferredHost
670
+ iterable = DefaultLoadBalancingPolicy._getPreferredHostFirst(
671
+ preferredHost,
672
+ iterable,
673
+ )
674
+ }
675
+
676
+ return callback(null, iterable)
677
+ }
678
+
679
+ /**
680
+ * Yields the preferred host first, followed by the host in the provided iterable
681
+ * @param preferredHost
682
+ * @param iterable
683
+ * @private
684
+ */
685
+ static *_getPreferredHostFirst(preferredHost, iterable) {
686
+ yield preferredHost
687
+
688
+ for (const host of iterable) {
689
+ if (host !== preferredHost) {
690
+ yield host
691
+ }
692
+ }
693
+ }
694
+
695
+ /**
696
+ * Yields the local hosts without the replicas already yielded
697
+ * @param {Array<Host>} [localReplicas] The local replicas that we should avoid to include again
698
+ * @private
699
+ */
700
+ *_getLocalHosts(localReplicas) {
701
+ // Use a local reference
702
+ const hosts = this._getFilteredLocalHosts()
703
+ const initialIndex = this._getIndex()
704
+
705
+ // indexOf() over an Array is a O(n) operation but given that there should be 3 to 7 replicas,
706
+ // it shouldn't be an expensive call. Additionally, this will only be executed when the local replicas
707
+ // have been exhausted in a lazy manner.
708
+ const canBeYield = localReplicas
709
+ ? (h) => localReplicas.indexOf(h) === -1
710
+ : (h) => true
711
+
712
+ for (let i = 0; i < hosts.length; i++) {
713
+ const h = hosts[(i + initialIndex) % hosts.length]
714
+ if (canBeYield(h) && h.isUp()) {
715
+ yield h
716
+ }
717
+ }
718
+ }
719
+
720
+ _getReplicasAndLocalHosts(keyspace, routingKey) {
721
+ let replicas = this._getReplicas(keyspace, routingKey)
722
+ if (replicas === null) {
723
+ return this._getLocalHosts()
724
+ }
725
+
726
+ const filteredReplicas = []
727
+ let newlyUpReplica = null
728
+ let newlyUpReplicaTimestamp = Number.MIN_SAFE_INTEGER
729
+ let unhealthyReplicas = 0
730
+
731
+ // Filter by DC, predicate and UP replicas
732
+ // Use the same iteration to perform other checks: whether if its newly UP or unhealthy
733
+ // As this is part of the hot path, we use a simple loop and avoid using Array.prototype.filter() + closure
734
+ for (let i = 0; i < replicas.length; i++) {
735
+ const h = replicas[i]
736
+ if (
737
+ !this._filter(h) ||
738
+ h.datacenter !== this.localDc ||
739
+ !h.isUp()
740
+ ) {
741
+ continue
742
+ }
743
+ const isUpSince = this._isHostNewlyUp(h)
744
+ if (isUpSince !== null && isUpSince > newlyUpReplicaTimestamp) {
745
+ newlyUpReplica = h
746
+ newlyUpReplicaTimestamp = isUpSince
747
+ }
748
+ if (newlyUpReplica === null && !this._healthCheck(h)) {
749
+ unhealthyReplicas++
750
+ }
751
+ filteredReplicas.push(h)
752
+ }
753
+
754
+ replicas = filteredReplicas
755
+
756
+ // Shuffle remaining local replicas
757
+ utils.shuffleArray(replicas)
758
+
759
+ if (replicas.length < 3) {
760
+ // Avoid reordering replicas of a set of 2 as we could be doing more harm than good
761
+ return this.yieldReplicasFirst(replicas)
762
+ }
763
+
764
+ let temp
765
+
766
+ if (newlyUpReplica === null) {
767
+ if (
768
+ unhealthyReplicas > 0 &&
769
+ unhealthyReplicas < Math.floor(replicas.length / 2 + 1)
770
+ ) {
771
+ // There is one or more unhealthy replicas and there is a majority of healthy replicas
772
+ this._sendUnhealthyToTheBack(replicas, unhealthyReplicas)
773
+ }
774
+ } else if (
775
+ (newlyUpReplica === replicas[0] ||
776
+ newlyUpReplica === replicas[1]) &&
777
+ Math.random() * 4 >= 1
778
+ ) {
779
+ // There is a newly UP replica and the replica in first or second position is the most recent replica
780
+ // marked as UP and dice roll 1d4!=1 -> Send it to the back of the Array
781
+ const index = newlyUpReplica === replicas[0] ? 0 : 1
782
+ temp = replicas[replicas.length - 1]
783
+ replicas[replicas.length - 1] = replicas[index]
784
+ replicas[index] = temp
785
+ }
786
+
787
+ if (this._compare(replicas[1], replicas[0]) > 0) {
788
+ // Power of two random choices
789
+ temp = replicas[0]
790
+ replicas[0] = replicas[1]
791
+ replicas[1] = temp
792
+ }
793
+
794
+ return this.yieldReplicasFirst(replicas)
795
+ }
796
+
797
+ /**
798
+ * Yields the local replicas followed by the rest of local nodes.
799
+ * @param {Array<Host>} replicas The local replicas
800
+ */
801
+ *yieldReplicasFirst(replicas) {
802
+ for (let i = 0; i < replicas.length; i++) {
803
+ yield replicas[i]
804
+ }
805
+ yield* this._getLocalHosts(replicas)
806
+ }
807
+
808
+ _isHostNewlyUp(h) {
809
+ return h.isUpSince !== null &&
810
+ Date.now() - h.isUpSince < newlyUpInterval
811
+ ? h.isUpSince
812
+ : null
813
+ }
814
+
815
+ /**
816
+ * Returns a boolean determining whether the host health is ok or not.
817
+ * A Host is considered unhealthy when there are enough items in the queue (10 items in-flight) but the
818
+ * Host is not responding to those requests.
819
+ * @param {Host} h
820
+ * @return {boolean}
821
+ * @private
822
+ */
823
+ _healthCheck(h) {
824
+ return !(h.getInFlight() >= 10 && h.getResponseCount() <= 1)
825
+ }
826
+
827
+ /**
828
+ * Compares to host and returns 1 if it needs to favor the first host otherwise, -1.
829
+ * @return {number}
830
+ * @private
831
+ */
832
+ _compare(h1, h2) {
833
+ return h1.getInFlight() < h2.getInFlight() ? 1 : -1
834
+ }
835
+
836
+ _getReplicas(keyspace, routingKey) {
837
+ return this._client.getReplicas(keyspace, routingKey)
838
+ }
839
+
840
+ /**
841
+ * Returns an Array of hosts filtered by DC and predicate.
842
+ * @returns {Array<Host>}
843
+ * @private
844
+ */
845
+ _getFilteredLocalHosts() {
846
+ if (this._filteredHosts === null) {
847
+ this._filteredHosts = this._hosts
848
+ .values()
849
+ .filter((h) => this._filter(h) && h.datacenter === this.localDc)
850
+ }
851
+ return this._filteredHosts
852
+ }
853
+
854
+ _getIndex() {
855
+ const result = this._index++
856
+ // Overflow protection
857
+ if (this._index === 0x7fffffff) {
858
+ this._index = 0
859
+ }
860
+ return result
861
+ }
862
+
863
+ _sendUnhealthyToTheBack(replicas, unhealthyReplicas) {
864
+ let counter = 0
865
+
866
+ // Start from the back, move backwards and stop once all unhealthy replicas are at the back
867
+ for (
868
+ let i = replicas.length - 1;
869
+ i >= 0 && counter < unhealthyReplicas;
870
+ i--
871
+ ) {
872
+ const host = replicas[i]
873
+ if (this._healthCheck(host)) {
874
+ continue
875
+ }
876
+
877
+ const targetIndex = replicas.length - 1 - counter
878
+ if (targetIndex !== i) {
879
+ const temp = replicas[targetIndex]
880
+ replicas[targetIndex] = host
881
+ replicas[i] = temp
882
+ }
883
+ counter++
884
+ }
885
+ }
886
+
887
+ _defaultFilter() {
888
+ return true
889
+ }
890
+
891
+ /**
892
+ * Gets an associative array containing the policy options.
893
+ */
894
+ getOptions() {
895
+ return new Map([
896
+ ["localDataCenter", this.localDc],
897
+ ["filterFunction", this._filter !== this._defaultFilter],
898
+ ])
899
+ }
900
+ }
901
+
902
+ /**
903
+ * Validates and sets the local data center to be used.
904
+ * @param {LoadBalancingPolicy} lbp
905
+ * @param {Client} client
906
+ * @param {HostMap} hosts
907
+ * @private
908
+ */
909
+ function setLocalDc(lbp, client, hosts) {
910
+ if (!(lbp instanceof LoadBalancingPolicy)) {
911
+ throw new errors.DriverInternalError(
912
+ "LoadBalancingPolicy instance was not provided",
913
+ )
914
+ }
915
+
916
+ if (client && client.options) {
917
+ if (lbp.localDc && !client.options.localDataCenter) {
918
+ client.log(
919
+ "info",
920
+ `Local data center '${lbp.localDc}' was provided as an argument to the load-balancing` +
921
+ ` policy. It is preferable to specify the local data center using 'localDataCenter' in Client` +
922
+ ` options instead when your application is targeting a single data center.`,
923
+ )
924
+ }
925
+
926
+ // If localDc is unset, use value set in client options.
927
+ lbp.localDc = lbp.localDc || client.options.localDataCenter
928
+ }
929
+
930
+ const dcs = getDataCenters(hosts)
931
+
932
+ if (!lbp.localDc) {
933
+ throw new errors.ArgumentError(
934
+ `'localDataCenter' is not defined in Client options and also was not specified in constructor.` +
935
+ ` At least one is required. Available DCs are: [${Array.from(dcs)}]`,
936
+ )
937
+ }
938
+
939
+ if (!dcs.has(lbp.localDc)) {
940
+ throw new errors.ArgumentError(
941
+ `Datacenter ${lbp.localDc} was not found. Available DCs are: [${Array.from(dcs)}]`,
942
+ )
943
+ }
944
+ }
945
+
946
+ function getDataCenters(hosts) {
947
+ return new Set(hosts.values().map((h) => h.datacenter))
948
+ }
949
+
950
+ export {
951
+ AllowListPolicy,
952
+ DCAwareRoundRobinPolicy,
953
+ DefaultLoadBalancingPolicy,
954
+ LoadBalancingPolicy,
955
+ RoundRobinPolicy,
956
+ TokenAwarePolicy,
957
+ // Deprecated: for backward compatibility only.
958
+ WhiteListPolicy,
959
+ }
960
+
961
+ export default {
962
+ AllowListPolicy,
963
+ DCAwareRoundRobinPolicy,
964
+ DefaultLoadBalancingPolicy,
965
+ LoadBalancingPolicy,
966
+ RoundRobinPolicy,
967
+ TokenAwarePolicy,
968
+ // Deprecated: for backward compatibility only.
969
+ WhiteListPolicy,
970
+ }