@ragestudio/scylla-odm 0.22.2 → 0.22.4
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 +11 -12
- 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,176 @@
|
|
|
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 { Long } from "../types/index.js"
|
|
20
|
+
import errors from "../errors.js"
|
|
21
|
+
|
|
22
|
+
/** @module policies/timestampGeneration */
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Defines the maximum date in milliseconds that can be represented in microseconds using Number ((2 ^ 53) / 1000)
|
|
26
|
+
* @const
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
const _maxSafeNumberDate = 9007199254740
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A long representing the value 1000
|
|
33
|
+
* @const
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
const _longOneThousand = Long.fromInt(1000)
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new instance of {@link TimestampGenerator}.
|
|
40
|
+
* @classdesc
|
|
41
|
+
* Generates client-side, microsecond-precision query timestamps.
|
|
42
|
+
* <p>
|
|
43
|
+
* Given that Cassandra uses those timestamps to resolve conflicts, implementations should generate
|
|
44
|
+
* monotonically increasing timestamps for successive invocations of {@link TimestampGenerator.next()}.
|
|
45
|
+
* </p>
|
|
46
|
+
* @constructor
|
|
47
|
+
*/
|
|
48
|
+
function TimestampGenerator() {}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Returns the next timestamp.
|
|
52
|
+
* <p>
|
|
53
|
+
* Implementors should enforce increasing monotonicity of timestamps, that is,
|
|
54
|
+
* a timestamp returned should always be strictly greater that any previously returned
|
|
55
|
+
* timestamp.
|
|
56
|
+
* <p/>
|
|
57
|
+
* <p>
|
|
58
|
+
* Implementors should strive to achieve microsecond precision in the best possible way,
|
|
59
|
+
* which is usually largely dependent on the underlying operating system's capabilities.
|
|
60
|
+
* </p>
|
|
61
|
+
* @param {Client} client The {@link Client} instance to generate timestamps to.
|
|
62
|
+
* @returns {Long|Number|null} the next timestamp (in microseconds). If it's equals to <code>null</code>, it won't be
|
|
63
|
+
* sent by the driver, letting the server to generate the timestamp.
|
|
64
|
+
* @abstract
|
|
65
|
+
*/
|
|
66
|
+
TimestampGenerator.prototype.next = function (client) {
|
|
67
|
+
throw new Error("next() must be implemented")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A timestamp generator that guarantees monotonically increasing timestamps and logs warnings when timestamps
|
|
72
|
+
* drift in the future.
|
|
73
|
+
* <p>
|
|
74
|
+
* {@link Date} has millisecond precision and client timestamps require microsecond precision. This generator
|
|
75
|
+
* keeps track of the last generated timestamp, and if the current time is within the same millisecond as the last,
|
|
76
|
+
* it fills the microsecond portion of the new timestamp with the value of an incrementing counter.
|
|
77
|
+
* </p>
|
|
78
|
+
* @param {Number} [warningThreshold] Determines how far in the future timestamps are allowed to drift before a
|
|
79
|
+
* warning is logged, expressed in milliseconds. Default: <code>1000</code>.
|
|
80
|
+
* @param {Number} [minLogInterval] In case of multiple log events, it determines the time separation between log
|
|
81
|
+
* events, expressed in milliseconds. Use 0 to disable. Default: <code>1000</code>.
|
|
82
|
+
* @extends {TimestampGenerator}
|
|
83
|
+
* @constructor
|
|
84
|
+
*/
|
|
85
|
+
function MonotonicTimestampGenerator(warningThreshold, minLogInterval) {
|
|
86
|
+
if (warningThreshold < 0) {
|
|
87
|
+
throw new errors.ArgumentError(
|
|
88
|
+
"warningThreshold can not be lower than 0",
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
this._warningThreshold = warningThreshold || 1000
|
|
92
|
+
this._minLogInterval = 1000
|
|
93
|
+
if (typeof minLogInterval === "number") {
|
|
94
|
+
// A value under 1 will disable logging
|
|
95
|
+
this._minLogInterval = minLogInterval
|
|
96
|
+
}
|
|
97
|
+
this._micros = -1
|
|
98
|
+
this._lastDate = 0
|
|
99
|
+
this._lastLogDate = 0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
util.inherits(MonotonicTimestampGenerator, TimestampGenerator)
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Returns the current time in milliseconds since UNIX epoch
|
|
106
|
+
* @returns {Number}
|
|
107
|
+
*/
|
|
108
|
+
MonotonicTimestampGenerator.prototype.getDate = function () {
|
|
109
|
+
return Date.now()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
MonotonicTimestampGenerator.prototype.next = function (client) {
|
|
113
|
+
let date = this.getDate()
|
|
114
|
+
let drifted = 0
|
|
115
|
+
if (date > this._lastDate) {
|
|
116
|
+
this._micros = 0
|
|
117
|
+
this._lastDate = date
|
|
118
|
+
return this._generateMicroseconds()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (date < this._lastDate) {
|
|
122
|
+
drifted = this._lastDate - date
|
|
123
|
+
date = this._lastDate
|
|
124
|
+
}
|
|
125
|
+
if (++this._micros === 1000) {
|
|
126
|
+
this._micros = 0
|
|
127
|
+
if (date === this._lastDate) {
|
|
128
|
+
// Move date 1 millisecond into the future
|
|
129
|
+
date++
|
|
130
|
+
drifted++
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const lastDate = this._lastDate
|
|
134
|
+
this._lastDate = date
|
|
135
|
+
const result = this._generateMicroseconds()
|
|
136
|
+
if (drifted >= this._warningThreshold) {
|
|
137
|
+
// Avoid logging an unbounded amount of times within a clock-skew event or during an interval when more than 1
|
|
138
|
+
// query is being issued by microsecond
|
|
139
|
+
const currentLogDate = Date.now()
|
|
140
|
+
if (
|
|
141
|
+
this._minLogInterval > 0 &&
|
|
142
|
+
this._lastLogDate + this._minLogInterval <= currentLogDate
|
|
143
|
+
) {
|
|
144
|
+
const message = util.format(
|
|
145
|
+
"Timestamp generated using current date was %d milliseconds behind the last generated timestamp (which " +
|
|
146
|
+
"millisecond portion was %d), the returned value (%s) is being artificially incremented to guarantee " +
|
|
147
|
+
"monotonicity.",
|
|
148
|
+
drifted,
|
|
149
|
+
lastDate,
|
|
150
|
+
result,
|
|
151
|
+
)
|
|
152
|
+
this._lastLogDate = currentLogDate
|
|
153
|
+
client.log("warning", message)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @private
|
|
161
|
+
* @returns {Number|Long}
|
|
162
|
+
*/
|
|
163
|
+
MonotonicTimestampGenerator.prototype._generateMicroseconds = function () {
|
|
164
|
+
if (this._lastDate < _maxSafeNumberDate) {
|
|
165
|
+
// We are safe until Jun 06 2255, its faster to perform this operations on Number than on Long
|
|
166
|
+
// We hope to have native int64 by then :)
|
|
167
|
+
return this._lastDate * 1000 + this._micros
|
|
168
|
+
}
|
|
169
|
+
return Long.fromNumber(this._lastDate)
|
|
170
|
+
.multiply(_longOneThousand)
|
|
171
|
+
.add(Long.fromInt(this._micros))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export { TimestampGenerator, MonotonicTimestampGenerator }
|
|
175
|
+
|
|
176
|
+
export default { TimestampGenerator, MonotonicTimestampGenerator }
|
|
@@ -0,0 +1,347 @@
|
|
|
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 utils from "./utils.js"
|
|
20
|
+
import types from "./types/index.js"
|
|
21
|
+
import promiseUtils from "./promise-utils.js"
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Encapsulates the logic for dealing with the different prepare request and response flows, including failover when
|
|
25
|
+
* trying to prepare a query.
|
|
26
|
+
*/
|
|
27
|
+
class PrepareHandler {
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new instance of PrepareHandler
|
|
30
|
+
* @param {Client} client
|
|
31
|
+
* @param {LoadBalancingPolicy} loadBalancing
|
|
32
|
+
*/
|
|
33
|
+
constructor(client, loadBalancing) {
|
|
34
|
+
this._client = client
|
|
35
|
+
this._loadBalancing = loadBalancing
|
|
36
|
+
this.logEmitter = client.options.logEmitter
|
|
37
|
+
this.log = utils.log
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Gets the query id and metadata for a prepared statement, preparing it on
|
|
42
|
+
* single host or on all hosts depending on the options.
|
|
43
|
+
* @param {Client} client
|
|
44
|
+
* @param {LoadBalancingPolicy} loadBalancing
|
|
45
|
+
* @param {String} query
|
|
46
|
+
* @param {String} keyspace
|
|
47
|
+
* @returns {Promise<{queryId, meta}>}
|
|
48
|
+
* @static
|
|
49
|
+
*/
|
|
50
|
+
static async getPrepared(client, loadBalancing, query, keyspace) {
|
|
51
|
+
const info = client.metadata.getPreparedInfo(keyspace, query)
|
|
52
|
+
if (info.queryId) {
|
|
53
|
+
return info
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (info.preparing) {
|
|
57
|
+
// It's already being prepared
|
|
58
|
+
return await promiseUtils.fromEvent(info, "prepared")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const instance = new PrepareHandler(client, loadBalancing)
|
|
62
|
+
return await instance._prepare(info, query, keyspace)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {Client} client
|
|
67
|
+
* @param {LoadBalancingPolicy} loadBalancing
|
|
68
|
+
* @param {Array} queries
|
|
69
|
+
* @param {String} keyspace
|
|
70
|
+
* @static
|
|
71
|
+
*/
|
|
72
|
+
static async getPreparedMultiple(client, loadBalancing, queries, keyspace) {
|
|
73
|
+
const result = []
|
|
74
|
+
|
|
75
|
+
for (const item of queries) {
|
|
76
|
+
let query
|
|
77
|
+
|
|
78
|
+
if (item) {
|
|
79
|
+
query = typeof item === "string" ? item : item.query
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (typeof query !== "string") {
|
|
83
|
+
throw new errors.ArgumentError("Query item should be a string")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const { queryId, meta } = await PrepareHandler.getPrepared(
|
|
87
|
+
client,
|
|
88
|
+
loadBalancing,
|
|
89
|
+
query,
|
|
90
|
+
keyspace,
|
|
91
|
+
)
|
|
92
|
+
result.push({
|
|
93
|
+
query,
|
|
94
|
+
params: utils.adaptNamedParamsPrepared(
|
|
95
|
+
item.params,
|
|
96
|
+
meta.columns,
|
|
97
|
+
),
|
|
98
|
+
queryId,
|
|
99
|
+
meta,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return result
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Prepares the query on a single host or on all hosts depending on the options.
|
|
108
|
+
* Uses the info 'prepared' event to emit the result.
|
|
109
|
+
* @param {Object} info
|
|
110
|
+
* @param {String} query
|
|
111
|
+
* @param {String} keyspace
|
|
112
|
+
* @returns {Promise<{queryId, meta}>}
|
|
113
|
+
*/
|
|
114
|
+
async _prepare(info, query, keyspace) {
|
|
115
|
+
info.preparing = true
|
|
116
|
+
let iterator
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
iterator = await promiseUtils.newQueryPlan(
|
|
120
|
+
this._loadBalancing,
|
|
121
|
+
keyspace,
|
|
122
|
+
null,
|
|
123
|
+
)
|
|
124
|
+
return await this._prepareWithQueryPlan(
|
|
125
|
+
info,
|
|
126
|
+
iterator,
|
|
127
|
+
query,
|
|
128
|
+
keyspace,
|
|
129
|
+
)
|
|
130
|
+
} catch (err) {
|
|
131
|
+
info.preparing = false
|
|
132
|
+
err.query = query
|
|
133
|
+
info.emit("prepared", err)
|
|
134
|
+
|
|
135
|
+
throw err
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Uses the query plan to prepare the query on the first host and optionally on the rest of the hosts.
|
|
141
|
+
* @param {Object} info
|
|
142
|
+
* @param {Iterator} iterator
|
|
143
|
+
* @param {String} query
|
|
144
|
+
* @param {String} keyspace
|
|
145
|
+
* @returns {Promise<{queryId, meta}>}
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
148
|
+
async _prepareWithQueryPlan(info, iterator, query, keyspace) {
|
|
149
|
+
const triedHosts = {}
|
|
150
|
+
|
|
151
|
+
while (true) {
|
|
152
|
+
const host = PrepareHandler.getNextHost(
|
|
153
|
+
iterator,
|
|
154
|
+
this._client.profileManager,
|
|
155
|
+
triedHosts,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if (host === null) {
|
|
159
|
+
throw new errors.NoHostAvailableError(triedHosts)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const connection = await PrepareHandler._borrowWithKeyspace(
|
|
164
|
+
host,
|
|
165
|
+
keyspace,
|
|
166
|
+
)
|
|
167
|
+
const response = await connection.prepareOnceAsync(
|
|
168
|
+
query,
|
|
169
|
+
keyspace,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if (this._client.options.prepareOnAllHosts) {
|
|
173
|
+
await this._prepareOnAllHosts(iterator, query, keyspace)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Set the prepared metadata
|
|
177
|
+
info.preparing = false
|
|
178
|
+
info.queryId = response.id
|
|
179
|
+
info.meta = response.meta
|
|
180
|
+
this._client.metadata.setPreparedById(info)
|
|
181
|
+
info.emit("prepared", null, info)
|
|
182
|
+
|
|
183
|
+
return info
|
|
184
|
+
} catch (err) {
|
|
185
|
+
triedHosts[host.address] = err
|
|
186
|
+
|
|
187
|
+
if (
|
|
188
|
+
!err.isSocketError &&
|
|
189
|
+
!(err instanceof errors.OperationTimedOutError)
|
|
190
|
+
) {
|
|
191
|
+
// There's no point in retrying syntax errors and other response errors
|
|
192
|
+
throw err
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Gets the next host from the query plan.
|
|
200
|
+
* @param {Iterator} iterator
|
|
201
|
+
* @param {ProfileManager} profileManager
|
|
202
|
+
* @param {Object} [triedHosts]
|
|
203
|
+
* @return {Host|null}
|
|
204
|
+
*/
|
|
205
|
+
static getNextHost(iterator, profileManager, triedHosts) {
|
|
206
|
+
let host
|
|
207
|
+
// Get a host that is UP in a sync loop
|
|
208
|
+
while (true) {
|
|
209
|
+
const item = iterator.next()
|
|
210
|
+
if (item.done) {
|
|
211
|
+
return null
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
host = item.value
|
|
215
|
+
|
|
216
|
+
// set the distance relative to the client first
|
|
217
|
+
const distance = profileManager.getDistance(host)
|
|
218
|
+
if (distance === types.distance.ignored) {
|
|
219
|
+
//If its marked as ignore by the load balancing policy, move on.
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (host.isUp()) {
|
|
224
|
+
break
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (triedHosts) {
|
|
228
|
+
triedHosts[host.address] = "Host considered as DOWN"
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return host
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Prepares all queries on a single host.
|
|
237
|
+
* @param {Host} host
|
|
238
|
+
* @param {Array} allPrepared
|
|
239
|
+
*/
|
|
240
|
+
static async prepareAllQueries(host, allPrepared) {
|
|
241
|
+
const anyKeyspaceQueries = []
|
|
242
|
+
|
|
243
|
+
const queriesByKeyspace = new Map()
|
|
244
|
+
allPrepared.forEach((info) => {
|
|
245
|
+
let arr
|
|
246
|
+
if (info.keyspace) {
|
|
247
|
+
arr = queriesByKeyspace.get(info.keyspace)
|
|
248
|
+
|
|
249
|
+
if (!arr) {
|
|
250
|
+
arr = []
|
|
251
|
+
queriesByKeyspace.set(info.keyspace, arr)
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
arr = anyKeyspaceQueries
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
arr.push(info.query)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
for (const [keyspace, queries] of queriesByKeyspace) {
|
|
261
|
+
await PrepareHandler._borrowAndPrepare(host, keyspace, queries)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
await PrepareHandler._borrowAndPrepare(host, null, anyKeyspaceQueries)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Borrows a connection from the host and prepares the queries provided.
|
|
269
|
+
* @param {Host} host
|
|
270
|
+
* @param {String} keyspace
|
|
271
|
+
* @param {Array} queries
|
|
272
|
+
* @returns {Promise<void>}
|
|
273
|
+
* @private
|
|
274
|
+
*/
|
|
275
|
+
static async _borrowAndPrepare(host, keyspace, queries) {
|
|
276
|
+
if (queries.length === 0) {
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const connection = await PrepareHandler._borrowWithKeyspace(
|
|
281
|
+
host,
|
|
282
|
+
keyspace,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
for (const query of queries) {
|
|
286
|
+
await connection.prepareOnceAsync(query, keyspace)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Borrows a connection and changes the active keyspace on the connection, if needed.
|
|
292
|
+
* It does not perform any retry or error handling.
|
|
293
|
+
* @param {Host!} host
|
|
294
|
+
* @param {string} keyspace
|
|
295
|
+
* @returns {Promise<Connection>}
|
|
296
|
+
* @throws {errors.BusyConnectionError} When the connection is busy.
|
|
297
|
+
* @throws {errors.ResponseError} For invalid keyspaces.
|
|
298
|
+
* @throws {Error} For socket errors.
|
|
299
|
+
* @private
|
|
300
|
+
*/
|
|
301
|
+
static async _borrowWithKeyspace(host, keyspace) {
|
|
302
|
+
const connection = host.borrowConnection()
|
|
303
|
+
|
|
304
|
+
if (keyspace && connection.keyspace !== keyspace) {
|
|
305
|
+
await connection.changeKeyspace(keyspace)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return connection
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Prepares the provided query on all hosts, except the host provided.
|
|
313
|
+
* @param {Iterator} iterator
|
|
314
|
+
* @param {String} query
|
|
315
|
+
* @param {String} keyspace
|
|
316
|
+
* @private
|
|
317
|
+
*/
|
|
318
|
+
_prepareOnAllHosts(iterator, query, keyspace) {
|
|
319
|
+
const queries = [query]
|
|
320
|
+
let h
|
|
321
|
+
const hosts = []
|
|
322
|
+
|
|
323
|
+
while (
|
|
324
|
+
(h = PrepareHandler.getNextHost(
|
|
325
|
+
iterator,
|
|
326
|
+
this._client.profileManager,
|
|
327
|
+
)) !== null
|
|
328
|
+
) {
|
|
329
|
+
hosts.push(h)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return Promise.all(
|
|
333
|
+
hosts.map((h) =>
|
|
334
|
+
PrepareHandler._borrowAndPrepare(h, keyspace, queries).catch(
|
|
335
|
+
(err) =>
|
|
336
|
+
this.log(
|
|
337
|
+
"verbose",
|
|
338
|
+
`Unexpected error while preparing query (${query}) on ${h.address}`,
|
|
339
|
+
err,
|
|
340
|
+
),
|
|
341
|
+
),
|
|
342
|
+
),
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export default PrepareHandler
|
|
@@ -0,0 +1,191 @@
|
|
|
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
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates a non-clearable timer that resolves the promise once elapses.
|
|
21
|
+
* @param {number} ms
|
|
22
|
+
* @returns {Promise<void>}
|
|
23
|
+
*/
|
|
24
|
+
function delay(ms) {
|
|
25
|
+
return new Promise((r) => setTimeout(r, ms || 0))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a Promise that gets resolved or rejected based on an event.
|
|
30
|
+
* @param {object} emitter
|
|
31
|
+
* @param {string} eventName
|
|
32
|
+
* @returns {Promise}
|
|
33
|
+
*/
|
|
34
|
+
function fromEvent(emitter, eventName) {
|
|
35
|
+
return new Promise((resolve, reject) =>
|
|
36
|
+
emitter.once(eventName, (err, result) => {
|
|
37
|
+
if (err) {
|
|
38
|
+
reject(err)
|
|
39
|
+
} else {
|
|
40
|
+
resolve(result)
|
|
41
|
+
}
|
|
42
|
+
}),
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a Promise from a callback based function.
|
|
48
|
+
* @param {Function} fn
|
|
49
|
+
* @returns {Promise}
|
|
50
|
+
*/
|
|
51
|
+
function fromCallback(fn) {
|
|
52
|
+
return new Promise((resolve, reject) =>
|
|
53
|
+
fn((err, result) => {
|
|
54
|
+
if (err) {
|
|
55
|
+
reject(err)
|
|
56
|
+
} else {
|
|
57
|
+
resolve(result)
|
|
58
|
+
}
|
|
59
|
+
}),
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Gets a function that has the signature of a callback that invokes the appropriate promise handler parameters.
|
|
65
|
+
* @param {Function} resolve
|
|
66
|
+
* @param {Function} reject
|
|
67
|
+
* @returns {Function}
|
|
68
|
+
*/
|
|
69
|
+
function getCallback(resolve, reject) {
|
|
70
|
+
return function (err, result) {
|
|
71
|
+
if (err) {
|
|
72
|
+
reject(err)
|
|
73
|
+
} else {
|
|
74
|
+
resolve(result)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function invokeSequentially(info, length, fn) {
|
|
80
|
+
let index
|
|
81
|
+
while ((index = info.counter++) < length) {
|
|
82
|
+
await fn(index)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Invokes the new query plan of the load balancing policy and returns a Promise.
|
|
88
|
+
* @param {LoadBalancingPolicy} lbp The load balancing policy.
|
|
89
|
+
* @param {String} keyspace Name of currently logged keyspace at <code>Client</code> level.
|
|
90
|
+
* @param {ExecutionOptions|null} executionOptions The information related to the execution of the request.
|
|
91
|
+
* @returns {Promise<Iterator>}
|
|
92
|
+
*/
|
|
93
|
+
function newQueryPlan(lbp, keyspace, executionOptions) {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
lbp.newQueryPlan(keyspace, executionOptions, (err, iterator) => {
|
|
96
|
+
if (err) {
|
|
97
|
+
reject(err)
|
|
98
|
+
} else {
|
|
99
|
+
resolve(iterator)
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Method that handles optional callbacks (dual promise and callback support).
|
|
107
|
+
* When callback is undefined it returns the promise.
|
|
108
|
+
* When using a callback, it will use it as handlers of the continuation of the promise.
|
|
109
|
+
* @param {Promise} promise
|
|
110
|
+
* @param {Function?} callback
|
|
111
|
+
* @returns {Promise|undefined}
|
|
112
|
+
*/
|
|
113
|
+
function optionalCallback(promise, callback) {
|
|
114
|
+
if (!callback) {
|
|
115
|
+
return promise
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
toCallback(promise, callback)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Invokes the provided function multiple times, considering the concurrency level limit.
|
|
123
|
+
* @param {Number} count
|
|
124
|
+
* @param {Number} limit
|
|
125
|
+
* @param {Function} fn
|
|
126
|
+
* @returns {Promise}
|
|
127
|
+
*/
|
|
128
|
+
function times(count, limit, fn) {
|
|
129
|
+
if (limit > count) {
|
|
130
|
+
limit = count
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const promises = new Array(limit)
|
|
134
|
+
|
|
135
|
+
const info = {
|
|
136
|
+
counter: 0,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < limit; i++) {
|
|
140
|
+
promises[i] = invokeSequentially(info, count, fn)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return Promise.all(promises)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Deals with unexpected rejections in order to avoid the unhandled promise rejection warning or failure.
|
|
148
|
+
* @param {Promise} promise
|
|
149
|
+
* @returns {undefined}
|
|
150
|
+
*/
|
|
151
|
+
function toBackground(promise) {
|
|
152
|
+
promise.catch(() => {})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Invokes the callback once outside the promise chain the promise is resolved or rejected.
|
|
157
|
+
* @param {Promise} promise
|
|
158
|
+
* @param {Function?} callback
|
|
159
|
+
* @returns {undefined}
|
|
160
|
+
*/
|
|
161
|
+
function toCallback(promise, callback) {
|
|
162
|
+
promise.then(
|
|
163
|
+
(result) => process.nextTick(() => callback(null, result)),
|
|
164
|
+
// Avoid marking the promise as rejected
|
|
165
|
+
(err) => process.nextTick(() => callback(err)),
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export {
|
|
170
|
+
delay,
|
|
171
|
+
fromCallback,
|
|
172
|
+
fromEvent,
|
|
173
|
+
getCallback,
|
|
174
|
+
newQueryPlan,
|
|
175
|
+
optionalCallback,
|
|
176
|
+
times,
|
|
177
|
+
toBackground,
|
|
178
|
+
toCallback,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export default {
|
|
182
|
+
delay,
|
|
183
|
+
fromCallback,
|
|
184
|
+
fromEvent,
|
|
185
|
+
getCallback,
|
|
186
|
+
newQueryPlan,
|
|
187
|
+
optionalCallback,
|
|
188
|
+
times,
|
|
189
|
+
toBackground,
|
|
190
|
+
toCallback,
|
|
191
|
+
}
|