@ragestudio/scylla-odm 0.22.2 → 0.22.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/batch/index.d.ts +3 -3
- package/batch/index.d.ts.map +1 -1
- package/client.d.ts +6 -5
- package/client.d.ts.map +1 -1
- package/client.js +7 -7
- package/client.js.map +1 -1
- package/cql_gen/create_table.d.ts +1 -1
- package/cql_gen/create_table.d.ts.map +1 -1
- package/document/index.d.ts +3 -3
- package/document/index.d.ts.map +1 -1
- package/driver/LICENSE.txt +177 -0
- package/driver/NOTICE.txt +67 -0
- package/driver/auth/index.d.ts +37 -0
- package/driver/auth/index.js +37 -0
- package/driver/auth/no-auth-provider.js +73 -0
- package/driver/auth/plain-text-auth-provider.js +81 -0
- package/driver/auth/provider.js +77 -0
- package/driver/client-options.js +442 -0
- package/driver/client.js +1267 -0
- package/driver/concurrent/index.d.ts +49 -0
- package/driver/concurrent/index.js +366 -0
- package/driver/connection.js +1034 -0
- package/driver/control-connection.js +1282 -0
- package/driver/encoder.js +2316 -0
- package/driver/errors.js +223 -0
- package/driver/execution-options.js +612 -0
- package/driver/execution-profile.js +274 -0
- package/driver/host-connection-pool.js +587 -0
- package/driver/host.js +699 -0
- package/driver/index.d.ts +387 -0
- package/driver/index.js +81 -0
- package/driver/mapping/cache.js +214 -0
- package/driver/mapping/doc-info-adapter.js +171 -0
- package/driver/mapping/index.d.ts +219 -0
- package/driver/mapping/index.js +57 -0
- package/driver/mapping/mapper.js +225 -0
- package/driver/mapping/mapping-handler.js +641 -0
- package/driver/mapping/model-batch-item.js +215 -0
- package/driver/mapping/model-batch-mapper.js +141 -0
- package/driver/mapping/model-mapper.js +315 -0
- package/driver/mapping/model-mapping-info.js +225 -0
- package/driver/mapping/object-selector.js +417 -0
- package/driver/mapping/q.js +156 -0
- package/driver/mapping/query-generator.js +556 -0
- package/driver/mapping/result-mapper.js +123 -0
- package/driver/mapping/result.js +139 -0
- package/driver/mapping/table-mappings.js +133 -0
- package/driver/mapping/tree.js +160 -0
- package/driver/metadata/aggregate.js +79 -0
- package/driver/metadata/client-state.js +119 -0
- package/driver/metadata/data-collection.js +182 -0
- package/driver/metadata/event-debouncer.js +174 -0
- package/driver/metadata/index.d.ts +276 -0
- package/driver/metadata/index.js +1156 -0
- package/driver/metadata/materialized-view.js +49 -0
- package/driver/metadata/schema-function.js +98 -0
- package/driver/metadata/schema-index.js +166 -0
- package/driver/metadata/schema-parser.js +1399 -0
- package/driver/metadata/table-metadata.js +77 -0
- package/driver/operation-state.js +206 -0
- package/driver/policies/address-resolution.js +145 -0
- package/driver/policies/index.d.ts +241 -0
- package/driver/policies/index.js +110 -0
- package/driver/policies/load-balancing.js +970 -0
- package/driver/policies/reconnection.js +166 -0
- package/driver/policies/retry.js +326 -0
- package/driver/policies/speculative-execution.js +150 -0
- package/driver/policies/timestamp-generation.js +176 -0
- package/driver/prepare-handler.js +347 -0
- package/driver/promise-utils.js +191 -0
- package/driver/readers.js +624 -0
- package/driver/request-execution.js +644 -0
- package/driver/request-handler.js +332 -0
- package/driver/requests.js +618 -0
- package/driver/stream-id-stack.js +209 -0
- package/driver/streams.js +745 -0
- package/driver/token.js +325 -0
- package/driver/tokenizer.js +631 -0
- package/driver/types/big-decimal.js +282 -0
- package/driver/types/duration.js +576 -0
- package/driver/types/index.d.ts +486 -0
- package/driver/types/index.js +733 -0
- package/driver/types/inet-address.js +262 -0
- package/driver/types/integer.js +818 -0
- package/driver/types/local-date.js +280 -0
- package/driver/types/local-time.js +299 -0
- package/driver/types/mutable-long.js +385 -0
- package/driver/types/protocol-version.js +391 -0
- package/driver/types/result-set.js +287 -0
- package/driver/types/result-stream.js +164 -0
- package/driver/types/row.js +85 -0
- package/driver/types/time-uuid.js +414 -0
- package/driver/types/tuple.js +103 -0
- package/driver/types/uuid.js +160 -0
- package/driver/types/vector.js +130 -0
- package/driver/types/version-number.js +153 -0
- package/driver/utils.js +1485 -0
- package/driver/writers.js +350 -0
- package/global.d.ts +1 -1
- package/global.d.ts.map +1 -1
- package/index.d.ts +6 -6
- package/index.d.ts.map +1 -1
- package/index.js +6 -6
- package/index.js.map +1 -1
- package/migrate/index.d.ts +1 -1
- package/migrate/index.d.ts.map +1 -1
- package/migrate/index.js +1 -1
- package/migrate/index.js.map +1 -1
- package/model/index.d.ts +6 -6
- package/model/index.d.ts.map +1 -1
- package/model/index.js +10 -10
- package/model/index.js.map +1 -1
- package/operations/countAll.d.ts +1 -1
- package/operations/countAll.d.ts.map +1 -1
- package/operations/delete.d.ts +3 -4
- package/operations/delete.d.ts.map +1 -1
- package/operations/delete.js +1 -1
- package/operations/delete.js.map +1 -1
- package/operations/find.d.ts +2 -2
- package/operations/find.d.ts.map +1 -1
- package/operations/find.js +1 -1
- package/operations/find.js.map +1 -1
- package/operations/findOne.d.ts +2 -2
- package/operations/findOne.d.ts.map +1 -1
- package/operations/findOne.js +1 -1
- package/operations/findOne.js.map +1 -1
- package/operations/insert.d.ts +3 -3
- package/operations/insert.d.ts.map +1 -1
- package/operations/insert.js +2 -2
- package/operations/insert.js.map +1 -1
- package/operations/sync.d.ts +1 -1
- package/operations/sync.d.ts.map +1 -1
- package/operations/sync.js +1 -1
- package/operations/sync.js.map +1 -1
- package/operations/tableExists.d.ts +1 -1
- package/operations/tableExists.d.ts.map +1 -1
- package/operations/update.d.ts +3 -3
- package/operations/update.d.ts.map +1 -1
- package/operations/update.js +2 -2
- package/operations/update.js.map +1 -1
- package/package.json +4 -12
- package/schema/index.d.ts +1 -1
- package/schema/index.d.ts.map +1 -1
- package/types.d.ts +4 -4
- package/types.d.ts.map +1 -1
- package/utils/queryParser.d.ts +1 -1
- package/utils/queryParser.d.ts.map +1 -1
- package/utils/queryParser.js +1 -1
- package/utils/queryParser.js.map +1 -1
- package/utils/typeChecker.d.ts +1 -1
- package/utils/typeChecker.d.ts.map +1 -1
- package/utils/typeChecker.js +1 -1
- package/utils/typeChecker.js.map +1 -1
|
@@ -0,0 +1,587 @@
|
|
|
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 events from "events"
|
|
20
|
+
|
|
21
|
+
import Connection from "./connection.js"
|
|
22
|
+
import utils from "./utils.js"
|
|
23
|
+
import promiseUtils from "./promise-utils.js"
|
|
24
|
+
import errors from "./errors.js"
|
|
25
|
+
import clientOptions from "./client-options.js"
|
|
26
|
+
|
|
27
|
+
// Used to get the index of the connection with less in-flight requests
|
|
28
|
+
let connectionIndex = 0
|
|
29
|
+
const connectionIndexOverflow = Math.pow(2, 15)
|
|
30
|
+
|
|
31
|
+
let defaultOptions
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Represents the possible states of the pool.
|
|
35
|
+
* Possible state transitions:
|
|
36
|
+
* - From initial to closing: The pool must be closed because the host is ignored.
|
|
37
|
+
* - From initial to shuttingDown: The pool is being shutdown as a result of a client shutdown.
|
|
38
|
+
* - From closing to initial state: The pool finished closing connections (is now ignored) and it resets to
|
|
39
|
+
* initial state in case the host is marked as local/remote in the future.
|
|
40
|
+
* - From closing to shuttingDown (rare): It was marked as ignored, now the client is being shutdown.
|
|
41
|
+
* - From shuttingDown to shutdown: Finished shutting down, the pool should not be reused.
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
const state = {
|
|
45
|
+
// Initial state: open / opening / ready to be opened
|
|
46
|
+
initial: 0,
|
|
47
|
+
// When the pool is being closed as part of a distance change
|
|
48
|
+
closing: 1,
|
|
49
|
+
// When the pool is being shutdown for good
|
|
50
|
+
shuttingDown: 2,
|
|
51
|
+
// When the pool has being shutdown
|
|
52
|
+
shutDown: 4,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Represents a pool of connections to a host
|
|
57
|
+
*/
|
|
58
|
+
class HostConnectionPool extends events.EventEmitter {
|
|
59
|
+
/**
|
|
60
|
+
* Creates a new instance of HostConnectionPool.
|
|
61
|
+
* @param {Host} host
|
|
62
|
+
* @param {Number} protocolVersion Initial protocol version
|
|
63
|
+
* @extends EventEmitter
|
|
64
|
+
*/
|
|
65
|
+
constructor(host, protocolVersion) {
|
|
66
|
+
super()
|
|
67
|
+
this._address = host.address
|
|
68
|
+
this._newConnectionTimeout = null
|
|
69
|
+
this._state = state.initial
|
|
70
|
+
this._opening = false
|
|
71
|
+
this._host = host
|
|
72
|
+
this.responseCounter = 0
|
|
73
|
+
this.options = host.options
|
|
74
|
+
this.protocolVersion = protocolVersion
|
|
75
|
+
this.coreConnectionsLength = 1
|
|
76
|
+
/**
|
|
77
|
+
* An immutable array of connections
|
|
78
|
+
* @type {Array.<Connection>}
|
|
79
|
+
*/
|
|
80
|
+
this.connections = utils.emptyArray
|
|
81
|
+
this.setMaxListeners(0)
|
|
82
|
+
this.log = utils.log
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getInFlight() {
|
|
86
|
+
const length = this.connections.length
|
|
87
|
+
if (length === 1) {
|
|
88
|
+
return this.connections[0].getInFlight()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let sum = 0
|
|
92
|
+
for (let i = 0; i < length; i++) {
|
|
93
|
+
sum += this.connections[i].getInFlight()
|
|
94
|
+
}
|
|
95
|
+
return sum
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Gets the least busy connection from the pool.
|
|
100
|
+
* @param {Connection} [previousConnection] When provided, the pool should attempt to obtain a different connection.
|
|
101
|
+
* @returns {Connection!}
|
|
102
|
+
* @throws {Error}
|
|
103
|
+
* @throws {BusyConnectionError}
|
|
104
|
+
*/
|
|
105
|
+
borrowConnection(previousConnection) {
|
|
106
|
+
if (this.connections.length === 0) {
|
|
107
|
+
throw new Error("No connection available")
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const maxRequests = this.options.pooling.maxRequestsPerConnection
|
|
111
|
+
const c = HostConnectionPool.minInFlight(
|
|
112
|
+
this.connections,
|
|
113
|
+
maxRequests,
|
|
114
|
+
previousConnection,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if (c.getInFlight() >= maxRequests) {
|
|
118
|
+
throw new errors.BusyConnectionError(
|
|
119
|
+
this._address,
|
|
120
|
+
maxRequests,
|
|
121
|
+
this.connections.length,
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return c
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Gets the connection with the minimum number of in-flight requests.
|
|
130
|
+
* Only checks for 2 connections (round-robin) and gets the one with minimum in-flight requests, as long as
|
|
131
|
+
* the amount of in-flight requests is lower than maxRequests.
|
|
132
|
+
* @param {Array.<Connection>} connections
|
|
133
|
+
* @param {Number} maxRequests
|
|
134
|
+
* @param {Connection} previousConnection When provided, it will attempt to obtain a different connection.
|
|
135
|
+
* @returns {Connection!}
|
|
136
|
+
*/
|
|
137
|
+
static minInFlight(connections, maxRequests, previousConnection) {
|
|
138
|
+
const length = connections.length
|
|
139
|
+
if (length === 1) {
|
|
140
|
+
return connections[0]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Use a single index for all hosts as a simplified way to balance the load between connections
|
|
144
|
+
connectionIndex++
|
|
145
|
+
if (connectionIndex >= connectionIndexOverflow) {
|
|
146
|
+
connectionIndex = 0
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let current
|
|
150
|
+
for (
|
|
151
|
+
let index = connectionIndex;
|
|
152
|
+
index < connectionIndex + length;
|
|
153
|
+
index++
|
|
154
|
+
) {
|
|
155
|
+
current = connections[index % length]
|
|
156
|
+
if (current === previousConnection) {
|
|
157
|
+
// Increment the index and skip
|
|
158
|
+
current = connections[++index % length]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let next = connections[(index + 1) % length]
|
|
162
|
+
if (next === previousConnection) {
|
|
163
|
+
// Skip
|
|
164
|
+
next = connections[(index + 2) % length]
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (next.getInFlight() < current.getInFlight()) {
|
|
168
|
+
current = next
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (current.getInFlight() < maxRequests) {
|
|
172
|
+
// Check as few connections as possible, as long as the amount of in-flight
|
|
173
|
+
// requests is lower than maxRequests
|
|
174
|
+
break
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return current
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Creates all the connections in the pool and switches the keyspace of each connection if needed.
|
|
182
|
+
* @param {string} keyspace
|
|
183
|
+
*/
|
|
184
|
+
async warmup(keyspace) {
|
|
185
|
+
if (this.connections.length < this.coreConnectionsLength) {
|
|
186
|
+
while (this.connections.length < this.coreConnectionsLength) {
|
|
187
|
+
await this._attemptNewConnection()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.log(
|
|
191
|
+
"info",
|
|
192
|
+
`Connection pool to host ${this._address} created with ${this.connections.length} connection(s)`,
|
|
193
|
+
)
|
|
194
|
+
} else {
|
|
195
|
+
this.log(
|
|
196
|
+
"info",
|
|
197
|
+
`Connection pool to host ${this._address} contains ${this.connections.length} connection(s)`,
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (keyspace) {
|
|
202
|
+
try {
|
|
203
|
+
for (const connection of this.connections) {
|
|
204
|
+
await connection.changeKeyspace(keyspace)
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
// Log it and move on, it could be a momentary schema mismatch failure
|
|
208
|
+
this.log(
|
|
209
|
+
"warning",
|
|
210
|
+
`Connection(s) to host ${this._address} could not be switched to keyspace ${keyspace}`,
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** @returns {Connection} */
|
|
217
|
+
_createConnection() {
|
|
218
|
+
const endpointOrServerName = !this.options.sni
|
|
219
|
+
? this._address
|
|
220
|
+
: this._host.hostId.toString()
|
|
221
|
+
|
|
222
|
+
const c = new Connection(
|
|
223
|
+
endpointOrServerName,
|
|
224
|
+
this.protocolVersion,
|
|
225
|
+
this.options,
|
|
226
|
+
)
|
|
227
|
+
this._addListeners(c)
|
|
228
|
+
return c
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** @param {Connection} c */
|
|
232
|
+
_addListeners(c) {
|
|
233
|
+
c.on("responseDequeued", () => this.responseCounter++)
|
|
234
|
+
|
|
235
|
+
const self = this
|
|
236
|
+
function connectionErrorCallback() {
|
|
237
|
+
// The socket is not fully open / can not send heartbeat
|
|
238
|
+
self.remove(c)
|
|
239
|
+
}
|
|
240
|
+
c.on("idleRequestError", connectionErrorCallback)
|
|
241
|
+
c.on("socketClose", connectionErrorCallback)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
addExistingConnection(c) {
|
|
245
|
+
this._addListeners(c)
|
|
246
|
+
// Use a copy of the connections array
|
|
247
|
+
this.connections = this.connections.slice(0)
|
|
248
|
+
this.connections.push(c)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Prevents reconnection timeout from triggering
|
|
253
|
+
*/
|
|
254
|
+
clearNewConnectionAttempt() {
|
|
255
|
+
if (!this._newConnectionTimeout) {
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
clearTimeout(this._newConnectionTimeout)
|
|
259
|
+
this._newConnectionTimeout = null
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Tries to open a new connection.
|
|
264
|
+
* If a connection is being opened, it will resolve when the existing open task completes.
|
|
265
|
+
* @returns {Promise<void>}
|
|
266
|
+
*/
|
|
267
|
+
async _attemptNewConnection() {
|
|
268
|
+
if (this._opening) {
|
|
269
|
+
// Wait for the event to fire
|
|
270
|
+
return await promiseUtils.fromEvent(this, "open")
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
this._opening = true
|
|
274
|
+
|
|
275
|
+
const c = this._createConnection()
|
|
276
|
+
let err
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await c.openAsync()
|
|
280
|
+
} catch (e) {
|
|
281
|
+
err = e
|
|
282
|
+
this.log(
|
|
283
|
+
"warning",
|
|
284
|
+
`Connection to ${this._address} could not be created: ${err}`,
|
|
285
|
+
err,
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (this.isClosing()) {
|
|
290
|
+
this.log(
|
|
291
|
+
"info",
|
|
292
|
+
`Connection to ${this._address} opened successfully but pool was being closed`,
|
|
293
|
+
)
|
|
294
|
+
err = new Error("Connection closed")
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (!err) {
|
|
298
|
+
// Append the connection to the pool.
|
|
299
|
+
// Use a copy of the connections array.
|
|
300
|
+
const newConnections = this.connections.slice(0)
|
|
301
|
+
newConnections.push(c)
|
|
302
|
+
this.connections = newConnections
|
|
303
|
+
this.log(
|
|
304
|
+
"info",
|
|
305
|
+
`Connection to ${this._address} opened successfully`,
|
|
306
|
+
)
|
|
307
|
+
} else {
|
|
308
|
+
promiseUtils.toBackground(c.closeAsync())
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Notify that creation finished by setting the flag and emitting the event
|
|
312
|
+
this._opening = false
|
|
313
|
+
this.emit("open", err, c)
|
|
314
|
+
|
|
315
|
+
if (err) {
|
|
316
|
+
// Opening failed
|
|
317
|
+
throw err
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
attemptNewConnectionImmediate() {
|
|
322
|
+
const self = this
|
|
323
|
+
function openConnection() {
|
|
324
|
+
self.clearNewConnectionAttempt()
|
|
325
|
+
self.scheduleNewConnectionAttempt(0)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (this._state === state.initial) {
|
|
329
|
+
return openConnection()
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (this._state === state.closing) {
|
|
333
|
+
return this.once("close", openConnection)
|
|
334
|
+
}
|
|
335
|
+
// In the case the pool its being / has been shutdown for good
|
|
336
|
+
// Do not attempt to create a new connection.
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Closes the connection and removes a connection from the pool.
|
|
341
|
+
* @param {Connection} connection
|
|
342
|
+
*/
|
|
343
|
+
remove(connection) {
|
|
344
|
+
// locating an object by position in the array is O(n), but normally there should be between 1 to 8 connections.
|
|
345
|
+
const index = this.connections.indexOf(connection)
|
|
346
|
+
if (index < 0) {
|
|
347
|
+
// it was already removed from the connections and it's closing
|
|
348
|
+
return
|
|
349
|
+
}
|
|
350
|
+
// remove the connection from the pool, using an pool copy
|
|
351
|
+
const newConnections = this.connections.slice(0)
|
|
352
|
+
newConnections.splice(index, 1)
|
|
353
|
+
this.connections = newConnections
|
|
354
|
+
// close the connection
|
|
355
|
+
setImmediate(function removeClose() {
|
|
356
|
+
connection.close()
|
|
357
|
+
})
|
|
358
|
+
this.emit("remove")
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @param {Number} delay
|
|
363
|
+
*/
|
|
364
|
+
scheduleNewConnectionAttempt(delay) {
|
|
365
|
+
if (this.isClosing()) {
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const self = this
|
|
370
|
+
|
|
371
|
+
this._newConnectionTimeout = setTimeout(
|
|
372
|
+
function newConnectionTimeoutExpired() {
|
|
373
|
+
self._newConnectionTimeout = null
|
|
374
|
+
if (self.connections.length >= self.coreConnectionsLength) {
|
|
375
|
+
// new connection can be scheduled while a new connection is being opened
|
|
376
|
+
// the pool has the appropriate size
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (delay > 0 && self.options.sni) {
|
|
381
|
+
// We use delay > 0 as an indication that it's a reconnection.
|
|
382
|
+
// A reconnection schedule can use delay = 0 as well, but it's a good enough signal.
|
|
383
|
+
promiseUtils.toBackground(
|
|
384
|
+
self.options.sni.addressResolver
|
|
385
|
+
.refresh()
|
|
386
|
+
.then(() => self._attemptNewConnection()),
|
|
387
|
+
)
|
|
388
|
+
return
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
promiseUtils.toBackground(self._attemptNewConnection())
|
|
392
|
+
},
|
|
393
|
+
delay,
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
hasScheduledNewConnection() {
|
|
398
|
+
return !!this._newConnectionTimeout || this._opening
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Increases the size of the connection pool in the background, if needed.
|
|
403
|
+
*/
|
|
404
|
+
increaseSize() {
|
|
405
|
+
if (
|
|
406
|
+
this.connections.length < this.coreConnectionsLength &&
|
|
407
|
+
!this.hasScheduledNewConnection()
|
|
408
|
+
) {
|
|
409
|
+
// schedule the next connection in the background
|
|
410
|
+
this.scheduleNewConnectionAttempt(0)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Gets the amount of responses and resets the internal counter.
|
|
416
|
+
* @returns {number}
|
|
417
|
+
*/
|
|
418
|
+
getAndResetResponseCounter() {
|
|
419
|
+
const temp = this.responseCounter
|
|
420
|
+
this.responseCounter = 0
|
|
421
|
+
return temp
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Gets a boolean indicating if the pool is being closed / shutting down or has been shutdown.
|
|
426
|
+
*/
|
|
427
|
+
isClosing() {
|
|
428
|
+
return this._state !== state.initial
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Gracefully waits for all in-flight requests to finish and closes the pool.
|
|
433
|
+
*/
|
|
434
|
+
drainAndShutdown() {
|
|
435
|
+
if (this.isClosing()) {
|
|
436
|
+
// Its already closing / shutting down
|
|
437
|
+
return
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
this._state = state.closing
|
|
441
|
+
this.clearNewConnectionAttempt()
|
|
442
|
+
|
|
443
|
+
if (this.connections.length === 0) {
|
|
444
|
+
return this._afterClosing()
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const self = this
|
|
448
|
+
const connections = this.connections
|
|
449
|
+
this.connections = utils.emptyArray
|
|
450
|
+
let closedConnections = 0
|
|
451
|
+
this.log(
|
|
452
|
+
"info",
|
|
453
|
+
util.format(
|
|
454
|
+
"Draining and closing %d connections to %s",
|
|
455
|
+
connections.length,
|
|
456
|
+
this._address,
|
|
457
|
+
),
|
|
458
|
+
)
|
|
459
|
+
let wasClosed = false
|
|
460
|
+
// eslint-disable-next-line prefer-const
|
|
461
|
+
let checkShutdownTimeout
|
|
462
|
+
|
|
463
|
+
for (let i = 0; i < connections.length; i++) {
|
|
464
|
+
const c = connections[i]
|
|
465
|
+
if (c.getInFlight() === 0) {
|
|
466
|
+
getDelayedClose(c)()
|
|
467
|
+
continue
|
|
468
|
+
}
|
|
469
|
+
c.emitDrain = true
|
|
470
|
+
c.once("drain", getDelayedClose(c))
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function getDelayedClose(connection) {
|
|
474
|
+
return function delayedClose() {
|
|
475
|
+
connection.close()
|
|
476
|
+
if (++closedConnections < connections.length) {
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
if (wasClosed) {
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
wasClosed = true
|
|
483
|
+
if (checkShutdownTimeout) {
|
|
484
|
+
clearTimeout(checkShutdownTimeout)
|
|
485
|
+
}
|
|
486
|
+
self._afterClosing()
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Check that after sometime (readTimeout + 100ms) the connections have been drained
|
|
491
|
+
const delay =
|
|
492
|
+
(this.options.socketOptions.readTimeout ||
|
|
493
|
+
getDefaultOptions().socketOptions.readTimeout) + 100
|
|
494
|
+
checkShutdownTimeout = setTimeout(function checkShutdown() {
|
|
495
|
+
wasClosed = true
|
|
496
|
+
connections.forEach(function connectionEach(c) {
|
|
497
|
+
c.close()
|
|
498
|
+
})
|
|
499
|
+
self._afterClosing()
|
|
500
|
+
}, delay)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
_afterClosing() {
|
|
504
|
+
const self = this
|
|
505
|
+
|
|
506
|
+
function resetState() {
|
|
507
|
+
if (self._state === state.shuttingDown) {
|
|
508
|
+
self._state = state.shutDown
|
|
509
|
+
} else {
|
|
510
|
+
self._state = state.initial
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
self.emit("close")
|
|
514
|
+
|
|
515
|
+
if (self._state === state.shutDown) {
|
|
516
|
+
self.emit("shutdown")
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (this._opening) {
|
|
521
|
+
// The pool is growing, reset the state back to init once the open finished (without any new connection)
|
|
522
|
+
return this.once("open", resetState)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
resetState()
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* @returns {Promise<void>}
|
|
530
|
+
*/
|
|
531
|
+
async shutdown() {
|
|
532
|
+
this.clearNewConnectionAttempt()
|
|
533
|
+
|
|
534
|
+
if (!this.connections.length) {
|
|
535
|
+
this._state = state.shutDown
|
|
536
|
+
return
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const previousState = this._state
|
|
540
|
+
this._state = state.shuttingDown
|
|
541
|
+
|
|
542
|
+
if (
|
|
543
|
+
previousState === state.closing ||
|
|
544
|
+
previousState === state.shuttingDown
|
|
545
|
+
) {
|
|
546
|
+
// When previous state was closing, it will drain all connections and close them
|
|
547
|
+
// When previous state was "shuttingDown", it will close all the connections
|
|
548
|
+
// Once it's completed, shutdown event will be emitted
|
|
549
|
+
return promiseUtils.fromEvent(this, "shutdown")
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
await this._closeAllConnections()
|
|
553
|
+
|
|
554
|
+
this._state = state.shutDown
|
|
555
|
+
this.emit("shutdown")
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
async _closeAllConnections() {
|
|
559
|
+
const connections = this.connections
|
|
560
|
+
// point to an empty array
|
|
561
|
+
this.connections = utils.emptyArray
|
|
562
|
+
if (connections.length === 0) {
|
|
563
|
+
return
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
this.log(
|
|
567
|
+
"info",
|
|
568
|
+
util.format(
|
|
569
|
+
"Closing %d connections to %s",
|
|
570
|
+
connections.length,
|
|
571
|
+
this._address,
|
|
572
|
+
),
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
await Promise.all(connections.map((c) => c.closeAsync()))
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/** Lazily loads the default options */
|
|
580
|
+
function getDefaultOptions() {
|
|
581
|
+
if (defaultOptions === undefined) {
|
|
582
|
+
defaultOptions = clientOptions.defaultOptions()
|
|
583
|
+
}
|
|
584
|
+
return defaultOptions
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
export default HostConnectionPool
|