@clickhouse/client 1.18.0 → 1.18.2-head.084b623.1
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/README.md +67 -2
- package/dist/config.d.ts +8 -2
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/connection/create_connection.d.ts +2 -1
- package/dist/connection/create_connection.js +4 -1
- package/dist/connection/create_connection.js.map +1 -1
- package/dist/connection/node_base_connection.d.ts +8 -26
- package/dist/connection/node_base_connection.js +12 -486
- package/dist/connection/node_base_connection.js.map +1 -1
- package/dist/connection/node_custom_agent_connection.d.ts +2 -1
- package/dist/connection/node_custom_agent_connection.js.map +1 -1
- package/dist/connection/node_http_connection.d.ts +2 -1
- package/dist/connection/node_http_connection.js.map +1 -1
- package/dist/connection/node_https_connection.d.ts +2 -1
- package/dist/connection/node_https_connection.js.map +1 -1
- package/dist/connection/socket_pool.d.ts +43 -0
- package/dist/connection/socket_pool.js +623 -0
- package/dist/connection/socket_pool.js.map +1 -0
- package/dist/connection/stream.d.ts +9 -3
- package/dist/connection/stream.js +110 -1
- package/dist/connection/stream.js.map +1 -1
- package/dist/utils/stream.js +14 -13
- package/dist/utils/stream.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
|
@@ -6,37 +6,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.NodeBaseConnection = void 0;
|
|
7
7
|
const client_common_1 = require("@clickhouse/client-common");
|
|
8
8
|
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
-
const stream_1 = __importDefault(require("stream"));
|
|
10
|
-
const zlib_1 = __importDefault(require("zlib"));
|
|
11
9
|
const utils_1 = require("../utils");
|
|
12
|
-
const
|
|
13
|
-
const
|
|
10
|
+
const stream_1 = require("./stream");
|
|
11
|
+
const socket_pool_1 = require("./socket_pool");
|
|
14
12
|
class NodeBaseConnection {
|
|
15
13
|
params;
|
|
16
14
|
agent;
|
|
17
15
|
defaultAuthHeader;
|
|
18
16
|
defaultHeaders;
|
|
19
|
-
jsonHandling;
|
|
20
|
-
knownSockets = new WeakMap();
|
|
21
|
-
idleSocketTTL;
|
|
22
17
|
connectionId = crypto_1.default.randomUUID();
|
|
23
|
-
|
|
24
|
-
// For overflow concerns:
|
|
25
|
-
// node -e 'console.log(Number.MAX_SAFE_INTEGER / (1_000_000 * 60 * 60 * 24 * 366))'
|
|
26
|
-
// gives 284 years of continuous operation at 1M requests per second
|
|
27
|
-
// before overflowing the 53-bit integer
|
|
28
|
-
requestCounter = 0;
|
|
29
|
-
getNewSocketId() {
|
|
30
|
-
this.socketCounter += 1;
|
|
31
|
-
return `${this.connectionId}:${this.socketCounter}`;
|
|
32
|
-
}
|
|
33
|
-
getNewRequestId() {
|
|
34
|
-
this.requestCounter += 1;
|
|
35
|
-
return `${this.connectionId}:${this.requestCounter}`;
|
|
36
|
-
}
|
|
18
|
+
socketPool;
|
|
37
19
|
constructor(params, agent) {
|
|
38
20
|
this.params = params;
|
|
39
21
|
this.agent = agent;
|
|
22
|
+
this.socketPool = new socket_pool_1.SocketPool(this.connectionId, this.params, this.createClientRequest.bind(this), this.agent);
|
|
40
23
|
if (params.auth.type === 'Credentials') {
|
|
41
24
|
this.defaultAuthHeader = `Basic ${Buffer.from(`${params.auth.username}:${params.auth.password}`).toString('base64')}`;
|
|
42
25
|
}
|
|
@@ -51,18 +34,13 @@ class NodeBaseConnection {
|
|
|
51
34
|
Connection: this.params.keep_alive.enabled ? 'keep-alive' : 'close',
|
|
52
35
|
'User-Agent': (0, utils_1.getUserAgent)(this.params.application_id),
|
|
53
36
|
};
|
|
54
|
-
this.idleSocketTTL = params.keep_alive.idle_socket_ttl;
|
|
55
|
-
this.jsonHandling = params.json ?? {
|
|
56
|
-
parse: JSON.parse,
|
|
57
|
-
stringify: JSON.stringify,
|
|
58
|
-
};
|
|
59
37
|
}
|
|
60
38
|
async ping(params) {
|
|
61
39
|
const { log_writer, log_level } = this.params;
|
|
62
40
|
const query_id = this.getQueryId(params.query_id);
|
|
63
41
|
const { controller, controllerCleanup } = this.getAbortController(params);
|
|
64
|
-
let result;
|
|
65
42
|
try {
|
|
43
|
+
let result;
|
|
66
44
|
if (params.select) {
|
|
67
45
|
const searchParams = (0, client_common_1.toSearchParams)({
|
|
68
46
|
database: undefined,
|
|
@@ -70,9 +48,9 @@ class NodeBaseConnection {
|
|
|
70
48
|
query_id,
|
|
71
49
|
});
|
|
72
50
|
result = await this.request({
|
|
51
|
+
query: PingQuery,
|
|
73
52
|
method: 'GET',
|
|
74
53
|
url: (0, client_common_1.transformUrl)({ url: this.params.url, searchParams }),
|
|
75
|
-
query: PingQuery,
|
|
76
54
|
abort_signal: controller.signal,
|
|
77
55
|
headers: this.buildRequestHeaders(),
|
|
78
56
|
query_id,
|
|
@@ -82,17 +60,17 @@ class NodeBaseConnection {
|
|
|
82
60
|
}
|
|
83
61
|
else {
|
|
84
62
|
result = await this.request({
|
|
63
|
+
query: 'ping',
|
|
85
64
|
method: 'GET',
|
|
86
65
|
url: (0, client_common_1.transformUrl)({ url: this.params.url, pathname: '/ping' }),
|
|
87
66
|
abort_signal: controller.signal,
|
|
88
67
|
headers: this.buildRequestHeaders(),
|
|
89
|
-
query: 'ping',
|
|
90
68
|
query_id,
|
|
91
69
|
log_writer,
|
|
92
70
|
log_level,
|
|
93
71
|
}, 'Ping');
|
|
94
72
|
}
|
|
95
|
-
await (0,
|
|
73
|
+
await (0, stream_1.drainStreamInternal)({
|
|
96
74
|
op: 'Ping',
|
|
97
75
|
log_writer,
|
|
98
76
|
query_id,
|
|
@@ -205,7 +183,7 @@ class NodeBaseConnection {
|
|
|
205
183
|
log_writer,
|
|
206
184
|
log_level,
|
|
207
185
|
}, 'Insert');
|
|
208
|
-
await (0,
|
|
186
|
+
await (0, stream_1.drainStreamInternal)({
|
|
209
187
|
op: 'Insert',
|
|
210
188
|
log_writer,
|
|
211
189
|
query_id,
|
|
@@ -250,9 +228,6 @@ class NodeBaseConnection {
|
|
|
250
228
|
operation: 'Command',
|
|
251
229
|
connection_id: this.connectionId,
|
|
252
230
|
query_id,
|
|
253
|
-
query: this.params.unsafeLogUnredactedQueries
|
|
254
|
-
? params.query
|
|
255
|
-
: undefined,
|
|
256
231
|
},
|
|
257
232
|
});
|
|
258
233
|
}
|
|
@@ -280,7 +255,7 @@ class NodeBaseConnection {
|
|
|
280
255
|
}
|
|
281
256
|
// ignore the response stream and release the socket immediately
|
|
282
257
|
const drainStartTime = Date.now();
|
|
283
|
-
await (0,
|
|
258
|
+
await (0, stream_1.drainStreamInternal)({
|
|
284
259
|
op: 'Command',
|
|
285
260
|
log_writer,
|
|
286
261
|
query_id,
|
|
@@ -362,14 +337,8 @@ class NodeBaseConnection {
|
|
|
362
337
|
},
|
|
363
338
|
};
|
|
364
339
|
}
|
|
365
|
-
logRequestError({ op, err, query_id, query_params,
|
|
340
|
+
logRequestError({ op, err, query_id, query_params, extra_args, }) {
|
|
366
341
|
if (this.params.log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
|
|
367
|
-
// Redact query parameter from search params unless explicitly allowed
|
|
368
|
-
if (!this.params.unsafeLogUnredactedQueries && search_params) {
|
|
369
|
-
// Clone to avoid mutating the original search params
|
|
370
|
-
search_params = new URLSearchParams(search_params);
|
|
371
|
-
search_params.delete('query');
|
|
372
|
-
}
|
|
373
342
|
this.params.log_writer.error({
|
|
374
343
|
message: this.httpRequestErrorMessage(op),
|
|
375
344
|
err: err,
|
|
@@ -377,10 +346,6 @@ class NodeBaseConnection {
|
|
|
377
346
|
operation: op,
|
|
378
347
|
connection_id: this.connectionId,
|
|
379
348
|
query_id,
|
|
380
|
-
query: this.params.unsafeLogUnredactedQueries
|
|
381
|
-
? query_params.query
|
|
382
|
-
: undefined,
|
|
383
|
-
search_params: search_params?.toString(),
|
|
384
349
|
with_abort_signal: query_params.abort_signal !== undefined,
|
|
385
350
|
session_id: query_params.session_id,
|
|
386
351
|
...extra_args,
|
|
@@ -391,27 +356,6 @@ class NodeBaseConnection {
|
|
|
391
356
|
httpRequestErrorMessage(op) {
|
|
392
357
|
return `${op}: HTTP request error.`;
|
|
393
358
|
}
|
|
394
|
-
parseSummary(op, response) {
|
|
395
|
-
const summaryHeader = response.headers['x-clickhouse-summary'];
|
|
396
|
-
if (typeof summaryHeader === 'string') {
|
|
397
|
-
try {
|
|
398
|
-
return this.jsonHandling.parse(summaryHeader);
|
|
399
|
-
}
|
|
400
|
-
catch (err) {
|
|
401
|
-
if (this.params.log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
|
|
402
|
-
this.params.log_writer.error({
|
|
403
|
-
message: `${op}: failed to parse X-ClickHouse-Summary header.`,
|
|
404
|
-
args: {
|
|
405
|
-
operation: op,
|
|
406
|
-
connection_id: this.connectionId,
|
|
407
|
-
'X-ClickHouse-Summary': summaryHeader,
|
|
408
|
-
},
|
|
409
|
-
err: err,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
359
|
async runExec(params) {
|
|
416
360
|
const { log_writer, log_level } = this.params;
|
|
417
361
|
const query_id = params.query_id;
|
|
@@ -480,425 +424,7 @@ class NodeBaseConnection {
|
|
|
480
424
|
}
|
|
481
425
|
}
|
|
482
426
|
async request(params, op) {
|
|
483
|
-
|
|
484
|
-
// otherwise, we can occasionally get an expired socket, see https://github.com/ClickHouse/clickhouse-js/issues/294
|
|
485
|
-
await (0, client_common_1.sleep)(0);
|
|
486
|
-
const { log_writer, query_id, log_level } = params;
|
|
487
|
-
const currentStackTrace = this.params.capture_enhanced_stack_trace
|
|
488
|
-
? (0, client_common_1.getCurrentStackTrace)()
|
|
489
|
-
: undefined;
|
|
490
|
-
const requestTimeout = this.params.request_timeout;
|
|
491
|
-
return new Promise((resolve, reject) => {
|
|
492
|
-
const start = Date.now();
|
|
493
|
-
const request = this.createClientRequest(params);
|
|
494
|
-
const request_id = this.getNewRequestId();
|
|
495
|
-
function onError(e) {
|
|
496
|
-
removeRequestListeners();
|
|
497
|
-
const err = (0, client_common_1.enhanceStackTrace)(e, currentStackTrace);
|
|
498
|
-
reject(err);
|
|
499
|
-
}
|
|
500
|
-
let responseStream;
|
|
501
|
-
const onResponse = async (_response) => {
|
|
502
|
-
if (this.params.log_level <= client_common_1.ClickHouseLogLevel.DEBUG) {
|
|
503
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
504
|
-
const { authorization, host, ...headers } = request.getHeaders();
|
|
505
|
-
const duration = Date.now() - start;
|
|
506
|
-
// Redact query parameter from URL search params unless explicitly allowed
|
|
507
|
-
let searchParams = params.url.searchParams;
|
|
508
|
-
if (!this.params.unsafeLogUnredactedQueries) {
|
|
509
|
-
// Clone to avoid mutating the original search params
|
|
510
|
-
searchParams = new URLSearchParams(searchParams);
|
|
511
|
-
searchParams.delete('query');
|
|
512
|
-
}
|
|
513
|
-
this.params.log_writer.debug({
|
|
514
|
-
module: 'HTTP Adapter',
|
|
515
|
-
message: `${op}: got a response from ClickHouse`,
|
|
516
|
-
args: {
|
|
517
|
-
operation: op,
|
|
518
|
-
connection_id: this.connectionId,
|
|
519
|
-
query_id,
|
|
520
|
-
request_id,
|
|
521
|
-
request_method: params.method,
|
|
522
|
-
request_path: params.url.pathname,
|
|
523
|
-
request_params: searchParams.toString(),
|
|
524
|
-
request_headers: headers,
|
|
525
|
-
response_status: _response.statusCode,
|
|
526
|
-
response_headers: _response.headers,
|
|
527
|
-
response_time_ms: duration,
|
|
528
|
-
},
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
const tryDecompressResponseStream = params.try_decompress_response_stream ?? true;
|
|
532
|
-
const ignoreErrorResponse = params.ignore_error_response ?? false;
|
|
533
|
-
// even if the stream decompression is disabled, we have to decompress it in case of an error
|
|
534
|
-
const isFailedResponse = !(0, client_common_1.isSuccessfulResponse)(_response.statusCode);
|
|
535
|
-
if (tryDecompressResponseStream ||
|
|
536
|
-
(isFailedResponse && !ignoreErrorResponse)) {
|
|
537
|
-
const decompressionResult = (0, compression_1.decompressResponse)(_response, log_writer, log_level);
|
|
538
|
-
if ((0, compression_1.isDecompressionError)(decompressionResult)) {
|
|
539
|
-
const err = (0, client_common_1.enhanceStackTrace)(decompressionResult.error, currentStackTrace);
|
|
540
|
-
return reject(err);
|
|
541
|
-
}
|
|
542
|
-
responseStream = decompressionResult.response;
|
|
543
|
-
}
|
|
544
|
-
else {
|
|
545
|
-
responseStream = _response;
|
|
546
|
-
}
|
|
547
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
|
|
548
|
-
log_writer.trace({
|
|
549
|
-
message: `${op}: response stream created`,
|
|
550
|
-
args: {
|
|
551
|
-
operation: op,
|
|
552
|
-
connection_id: this.connectionId,
|
|
553
|
-
query_id,
|
|
554
|
-
request_id,
|
|
555
|
-
stream_state: {
|
|
556
|
-
readable: responseStream.readable,
|
|
557
|
-
readableEnded: responseStream.readableEnded,
|
|
558
|
-
readableLength: responseStream.readableLength,
|
|
559
|
-
},
|
|
560
|
-
is_failed_response: isFailedResponse,
|
|
561
|
-
will_decompress: tryDecompressResponseStream,
|
|
562
|
-
},
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
if (isFailedResponse && !ignoreErrorResponse) {
|
|
566
|
-
try {
|
|
567
|
-
const errorMessage = await (0, utils_1.getAsText)(responseStream);
|
|
568
|
-
const err = (0, client_common_1.enhanceStackTrace)((0, client_common_1.parseError)(errorMessage), currentStackTrace);
|
|
569
|
-
reject(err);
|
|
570
|
-
}
|
|
571
|
-
catch (e) {
|
|
572
|
-
// If the ClickHouse response is malformed
|
|
573
|
-
const err = (0, client_common_1.enhanceStackTrace)(e, currentStackTrace);
|
|
574
|
-
reject(err);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
else {
|
|
578
|
-
return resolve({
|
|
579
|
-
stream: responseStream,
|
|
580
|
-
summary: params.parse_summary
|
|
581
|
-
? this.parseSummary(op, _response)
|
|
582
|
-
: undefined,
|
|
583
|
-
response_headers: { ..._response.headers },
|
|
584
|
-
http_status_code: _response.statusCode ?? undefined,
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
function onAbort() {
|
|
589
|
-
// Prefer 'abort' event since it always triggered unlike 'error' and 'close'
|
|
590
|
-
// see the full sequence of events https://nodejs.org/api/http.html#httprequesturl-options-callback
|
|
591
|
-
removeRequestListeners();
|
|
592
|
-
request.once('error', function () {
|
|
593
|
-
/**
|
|
594
|
-
* catch "Error: ECONNRESET" error which shouldn't be reported to users.
|
|
595
|
-
* see the full sequence of events https://nodejs.org/api/http.html#httprequesturl-options-callback
|
|
596
|
-
* */
|
|
597
|
-
});
|
|
598
|
-
const err = (0, client_common_1.enhanceStackTrace)(new Error('The user aborted a request.'), currentStackTrace);
|
|
599
|
-
reject(err);
|
|
600
|
-
}
|
|
601
|
-
function onClose() {
|
|
602
|
-
// Adapter uses 'close' event to clean up listeners after the successful response.
|
|
603
|
-
// It's necessary in order to handle 'abort' and 'timeout' events while response is streamed.
|
|
604
|
-
// It's always the last event, according to https://nodejs.org/docs/latest-v14.x/api/http.html#http_http_request_url_options_callback
|
|
605
|
-
removeRequestListeners();
|
|
606
|
-
}
|
|
607
|
-
function pipeStream() {
|
|
608
|
-
// if request.end() was called due to no data to send
|
|
609
|
-
if (request.writableEnded) {
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
const bodyStream = (0, utils_1.isStream)(params.body)
|
|
613
|
-
? params.body
|
|
614
|
-
: stream_1.default.Readable.from([params.body]);
|
|
615
|
-
const callback = (e) => {
|
|
616
|
-
if (e) {
|
|
617
|
-
removeRequestListeners();
|
|
618
|
-
const err = (0, client_common_1.enhanceStackTrace)(e, currentStackTrace);
|
|
619
|
-
reject(err);
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
if (params.enable_request_compression) {
|
|
623
|
-
stream_1.default.pipeline(bodyStream, zlib_1.default.createGzip(), request, callback);
|
|
624
|
-
}
|
|
625
|
-
else {
|
|
626
|
-
stream_1.default.pipeline(bodyStream, request, callback);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
const onSocket = (socket) => {
|
|
630
|
-
try {
|
|
631
|
-
if (this.params.keep_alive.enabled &&
|
|
632
|
-
this.params.keep_alive.idle_socket_ttl > 0) {
|
|
633
|
-
const socketInfo = this.knownSockets.get(socket);
|
|
634
|
-
// It is the first time we've encountered this socket,
|
|
635
|
-
// so it doesn't have the idle timeout handler attached to it
|
|
636
|
-
if (socketInfo === undefined) {
|
|
637
|
-
const socket_id = this.getNewSocketId();
|
|
638
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
|
|
639
|
-
log_writer.trace({
|
|
640
|
-
message: `${op}: using a fresh socket, setting up a new 'free' listener`,
|
|
641
|
-
args: {
|
|
642
|
-
operation: op,
|
|
643
|
-
connection_id: this.connectionId,
|
|
644
|
-
query_id,
|
|
645
|
-
request_id,
|
|
646
|
-
socket_id,
|
|
647
|
-
},
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
const newSocketInfo = {
|
|
651
|
-
id: socket_id,
|
|
652
|
-
idle_timeout_handle: undefined,
|
|
653
|
-
usage_count: 1,
|
|
654
|
-
};
|
|
655
|
-
this.knownSockets.set(socket, newSocketInfo);
|
|
656
|
-
// When the request is complete and the socket is released,
|
|
657
|
-
// make sure that the socket is removed after `idleSocketTTL`.
|
|
658
|
-
socket.on('free', () => {
|
|
659
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
|
|
660
|
-
log_writer.trace({
|
|
661
|
-
message: `${op}: socket was released`,
|
|
662
|
-
args: {
|
|
663
|
-
operation: op,
|
|
664
|
-
connection_id: this.connectionId,
|
|
665
|
-
query_id,
|
|
666
|
-
request_id,
|
|
667
|
-
socket_id,
|
|
668
|
-
},
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
// Avoiding the built-in socket.timeout() method usage here,
|
|
672
|
-
// as we don't want to clash with the actual request timeout.
|
|
673
|
-
const idleTimeoutHandle = setTimeout(() => {
|
|
674
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
|
|
675
|
-
log_writer.trace({
|
|
676
|
-
message: `${op}: removing idle socket`,
|
|
677
|
-
args: {
|
|
678
|
-
operation: op,
|
|
679
|
-
connection_id: this.connectionId,
|
|
680
|
-
query_id,
|
|
681
|
-
request_id,
|
|
682
|
-
socket_id,
|
|
683
|
-
idle_socket_ttl_ms: this.idleSocketTTL,
|
|
684
|
-
},
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
this.knownSockets.delete(socket);
|
|
688
|
-
socket.destroy();
|
|
689
|
-
}, this.idleSocketTTL).unref();
|
|
690
|
-
newSocketInfo.idle_timeout_handle = idleTimeoutHandle;
|
|
691
|
-
});
|
|
692
|
-
const cleanup = (eventName) => () => {
|
|
693
|
-
const maybeSocketInfo = this.knownSockets.get(socket);
|
|
694
|
-
// clean up a possibly dangling idle timeout handle (preventing leaks)
|
|
695
|
-
if (maybeSocketInfo?.idle_timeout_handle) {
|
|
696
|
-
clearTimeout(maybeSocketInfo.idle_timeout_handle);
|
|
697
|
-
}
|
|
698
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
|
|
699
|
-
log_writer.trace({
|
|
700
|
-
message: `${op}: received '${eventName}' event, 'free' listener removed`,
|
|
701
|
-
args: {
|
|
702
|
-
operation: op,
|
|
703
|
-
connection_id: this.connectionId,
|
|
704
|
-
query_id,
|
|
705
|
-
request_id,
|
|
706
|
-
socket_id,
|
|
707
|
-
event: eventName,
|
|
708
|
-
},
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.WARN) {
|
|
712
|
-
if (responseStream && !responseStream.readableEnded) {
|
|
713
|
-
log_writer.warn({
|
|
714
|
-
message: `${op}: socket was closed or ended before the response was fully read. ` +
|
|
715
|
-
'This can potentially result in an uncaught ECONNRESET error! ' +
|
|
716
|
-
'Consider fully consuming, draining, or destroying the response stream.',
|
|
717
|
-
args: {
|
|
718
|
-
operation: op,
|
|
719
|
-
connection_id: this.connectionId,
|
|
720
|
-
query_id,
|
|
721
|
-
request_id,
|
|
722
|
-
socket_id,
|
|
723
|
-
event: eventName,
|
|
724
|
-
query: this.params.unsafeLogUnredactedQueries
|
|
725
|
-
? params.query
|
|
726
|
-
: undefined,
|
|
727
|
-
},
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
};
|
|
732
|
-
socket.once('end', cleanup('end'));
|
|
733
|
-
socket.once('close', cleanup('close'));
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
736
|
-
clearTimeout(socketInfo.idle_timeout_handle);
|
|
737
|
-
socketInfo.idle_timeout_handle = undefined;
|
|
738
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
|
|
739
|
-
log_writer.trace({
|
|
740
|
-
message: `${op}: reusing socket`,
|
|
741
|
-
args: {
|
|
742
|
-
operation: op,
|
|
743
|
-
connection_id: this.connectionId,
|
|
744
|
-
query_id,
|
|
745
|
-
request_id,
|
|
746
|
-
socket_id: socketInfo.id,
|
|
747
|
-
usage_count: socketInfo.usage_count,
|
|
748
|
-
},
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
socketInfo.usage_count++;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
catch (e) {
|
|
756
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
|
|
757
|
-
log_writer.error({
|
|
758
|
-
message: `${op}: an error occurred while housekeeping the idle sockets`,
|
|
759
|
-
err: e,
|
|
760
|
-
args: {
|
|
761
|
-
operation: op,
|
|
762
|
-
connection_id: this.connectionId,
|
|
763
|
-
query_id,
|
|
764
|
-
request_id,
|
|
765
|
-
},
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
// Socket is "prepared" with idle handlers, continue with our request
|
|
770
|
-
pipeStream();
|
|
771
|
-
// This is for request timeout only. Surprisingly, it is not always enough to set in the HTTP request.
|
|
772
|
-
// The socket won't be destroyed, and it will be returned to the pool.
|
|
773
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
|
|
774
|
-
const socketInfo = this.knownSockets.get(socket);
|
|
775
|
-
if (socketInfo) {
|
|
776
|
-
log_writer.trace({
|
|
777
|
-
message: `${op}: setting up request timeout`,
|
|
778
|
-
args: {
|
|
779
|
-
operation: op,
|
|
780
|
-
connection_id: this.connectionId,
|
|
781
|
-
query_id,
|
|
782
|
-
request_id,
|
|
783
|
-
socket_id: socketInfo.id,
|
|
784
|
-
timeout_ms: requestTimeout,
|
|
785
|
-
},
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
else {
|
|
789
|
-
log_writer.trace({
|
|
790
|
-
message: `${op}: setting up request timeout on a socket`,
|
|
791
|
-
args: {
|
|
792
|
-
operation: op,
|
|
793
|
-
connection_id: this.connectionId,
|
|
794
|
-
query_id,
|
|
795
|
-
request_id,
|
|
796
|
-
timeout_ms: requestTimeout,
|
|
797
|
-
},
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
socket.setTimeout(this.params.request_timeout, onTimeout);
|
|
802
|
-
};
|
|
803
|
-
const onTimeout = () => {
|
|
804
|
-
removeRequestListeners();
|
|
805
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
|
|
806
|
-
const socket = request.socket;
|
|
807
|
-
const maybeSocketInfo = socket
|
|
808
|
-
? this.knownSockets.get(socket)
|
|
809
|
-
: undefined;
|
|
810
|
-
const socketState = request.socket
|
|
811
|
-
? {
|
|
812
|
-
connecting: request.socket.connecting,
|
|
813
|
-
pending: request.socket.pending,
|
|
814
|
-
destroyed: request.socket.destroyed,
|
|
815
|
-
readyState: request.socket.readyState,
|
|
816
|
-
}
|
|
817
|
-
: undefined;
|
|
818
|
-
const responseStreamState = responseStream
|
|
819
|
-
? {
|
|
820
|
-
readable: responseStream.readable,
|
|
821
|
-
readableEnded: responseStream.readableEnded,
|
|
822
|
-
readableLength: responseStream.readableLength,
|
|
823
|
-
}
|
|
824
|
-
: undefined;
|
|
825
|
-
log_writer.trace({
|
|
826
|
-
message: `${op}: timeout occurred`,
|
|
827
|
-
args: {
|
|
828
|
-
operation: op,
|
|
829
|
-
connection_id: this.connectionId,
|
|
830
|
-
query_id,
|
|
831
|
-
request_id,
|
|
832
|
-
socket_id: maybeSocketInfo?.id,
|
|
833
|
-
timeout_ms: requestTimeout,
|
|
834
|
-
socket_state: socketState,
|
|
835
|
-
response_stream_state: responseStreamState,
|
|
836
|
-
has_response_stream: responseStream !== undefined,
|
|
837
|
-
},
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
const err = (0, client_common_1.enhanceStackTrace)(new Error('Timeout error.'), currentStackTrace);
|
|
841
|
-
try {
|
|
842
|
-
request.destroy();
|
|
843
|
-
}
|
|
844
|
-
catch (e) {
|
|
845
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
|
|
846
|
-
log_writer.error({
|
|
847
|
-
message: `${op}: An error occurred while destroying the request`,
|
|
848
|
-
err: e,
|
|
849
|
-
args: {
|
|
850
|
-
operation: op,
|
|
851
|
-
connection_id: this.connectionId,
|
|
852
|
-
query_id,
|
|
853
|
-
request_id,
|
|
854
|
-
},
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
reject(err);
|
|
859
|
-
};
|
|
860
|
-
function removeRequestListeners() {
|
|
861
|
-
if (request.socket !== null) {
|
|
862
|
-
request.socket.setTimeout(0); // reset previously set timeout
|
|
863
|
-
request.socket.removeListener('timeout', onTimeout);
|
|
864
|
-
}
|
|
865
|
-
request.removeListener('socket', onSocket);
|
|
866
|
-
request.removeListener('response', onResponse);
|
|
867
|
-
request.removeListener('error', onError);
|
|
868
|
-
request.removeListener('close', onClose);
|
|
869
|
-
if (params.abort_signal !== undefined) {
|
|
870
|
-
request.removeListener('abort', onAbort);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
request.on('socket', onSocket);
|
|
874
|
-
request.on('response', onResponse);
|
|
875
|
-
request.on('error', onError);
|
|
876
|
-
request.on('close', onClose);
|
|
877
|
-
if (params.abort_signal !== undefined) {
|
|
878
|
-
params.abort_signal.addEventListener('abort', onAbort, {
|
|
879
|
-
once: true,
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
if (!params.body) {
|
|
883
|
-
try {
|
|
884
|
-
return request.end();
|
|
885
|
-
}
|
|
886
|
-
catch (e) {
|
|
887
|
-
if (log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
|
|
888
|
-
log_writer.error({
|
|
889
|
-
message: `${op}: an error occurred while ending the request without body`,
|
|
890
|
-
err: e,
|
|
891
|
-
args: {
|
|
892
|
-
operation: op,
|
|
893
|
-
connection_id: this.connectionId,
|
|
894
|
-
query_id,
|
|
895
|
-
request_id,
|
|
896
|
-
},
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
});
|
|
427
|
+
return this.socketPool.request(params, op);
|
|
902
428
|
}
|
|
903
429
|
}
|
|
904
430
|
exports.NodeBaseConnection = NodeBaseConnection;
|