@clickhouse/client 1.17.0 → 1.18.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.
@@ -12,55 +12,31 @@ const utils_1 = require("../utils");
12
12
  const compression_1 = require("./compression");
13
13
  const stream_2 = require("./stream");
14
14
  class NodeBaseConnection {
15
+ params;
16
+ agent;
17
+ defaultAuthHeader;
18
+ defaultHeaders;
19
+ jsonHandling;
20
+ knownSockets = new WeakMap();
21
+ idleSocketTTL;
22
+ connectionId = crypto_1.default.randomUUID();
23
+ socketCounter = 0;
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
+ }
15
37
  constructor(params, agent) {
16
- Object.defineProperty(this, "params", {
17
- enumerable: true,
18
- configurable: true,
19
- writable: true,
20
- value: params
21
- });
22
- Object.defineProperty(this, "agent", {
23
- enumerable: true,
24
- configurable: true,
25
- writable: true,
26
- value: agent
27
- });
28
- Object.defineProperty(this, "defaultAuthHeader", {
29
- enumerable: true,
30
- configurable: true,
31
- writable: true,
32
- value: void 0
33
- });
34
- Object.defineProperty(this, "defaultHeaders", {
35
- enumerable: true,
36
- configurable: true,
37
- writable: true,
38
- value: void 0
39
- });
40
- Object.defineProperty(this, "jsonHandling", {
41
- enumerable: true,
42
- configurable: true,
43
- writable: true,
44
- value: void 0
45
- });
46
- Object.defineProperty(this, "logger", {
47
- enumerable: true,
48
- configurable: true,
49
- writable: true,
50
- value: void 0
51
- });
52
- Object.defineProperty(this, "knownSockets", {
53
- enumerable: true,
54
- configurable: true,
55
- writable: true,
56
- value: new WeakMap()
57
- });
58
- Object.defineProperty(this, "idleSocketTTL", {
59
- enumerable: true,
60
- configurable: true,
61
- writable: true,
62
- value: void 0
63
- });
38
+ this.params = params;
39
+ this.agent = agent;
64
40
  if (params.auth.type === 'Credentials') {
65
41
  this.defaultAuthHeader = `Basic ${Buffer.from(`${params.auth.username}:${params.auth.password}`).toString('base64')}`;
66
42
  }
@@ -75,7 +51,6 @@ class NodeBaseConnection {
75
51
  Connection: this.params.keep_alive.enabled ? 'keep-alive' : 'close',
76
52
  'User-Agent': (0, utils_1.getUserAgent)(this.params.application_id),
77
53
  };
78
- this.logger = params.log_writer;
79
54
  this.idleSocketTTL = params.keep_alive.idle_socket_ttl;
80
55
  this.jsonHandling = params.json ?? {
81
56
  parse: JSON.parse,
@@ -83,10 +58,11 @@ class NodeBaseConnection {
83
58
  };
84
59
  }
85
60
  async ping(params) {
61
+ const { log_writer, log_level } = this.params;
86
62
  const query_id = this.getQueryId(params.query_id);
87
63
  const { controller, controllerCleanup } = this.getAbortController(params);
88
- let result;
89
64
  try {
65
+ let result;
90
66
  if (params.select) {
91
67
  const searchParams = (0, client_common_1.toSearchParams)({
92
68
  database: undefined,
@@ -94,23 +70,34 @@ class NodeBaseConnection {
94
70
  query_id,
95
71
  });
96
72
  result = await this.request({
73
+ query: PingQuery,
97
74
  method: 'GET',
98
75
  url: (0, client_common_1.transformUrl)({ url: this.params.url, searchParams }),
99
- query: PingQuery,
100
76
  abort_signal: controller.signal,
101
77
  headers: this.buildRequestHeaders(),
78
+ query_id,
79
+ log_writer,
80
+ log_level,
102
81
  }, 'Ping');
103
82
  }
104
83
  else {
105
84
  result = await this.request({
85
+ query: 'ping',
106
86
  method: 'GET',
107
87
  url: (0, client_common_1.transformUrl)({ url: this.params.url, pathname: '/ping' }),
108
88
  abort_signal: controller.signal,
109
89
  headers: this.buildRequestHeaders(),
110
- query: 'ping',
90
+ query_id,
91
+ log_writer,
92
+ log_level,
111
93
  }, 'Ping');
112
94
  }
113
- await (0, stream_2.drainStream)(result.stream);
95
+ await (0, stream_2.drainStreamInternal)({
96
+ op: 'Ping',
97
+ log_writer,
98
+ query_id,
99
+ log_level,
100
+ }, result.stream);
114
101
  return { success: true };
115
102
  }
116
103
  catch (error) {
@@ -118,13 +105,16 @@ class NodeBaseConnection {
118
105
  // and we don't get unhandled error propagation later
119
106
  controller.abort('Ping failed');
120
107
  // not an error, as this might be semi-expected
121
- this.logger.warn({
122
- message: this.httpRequestErrorMessage('Ping'),
123
- err: error,
124
- args: {
125
- query_id,
126
- },
127
- });
108
+ if (log_level <= client_common_1.ClickHouseLogLevel.WARN) {
109
+ log_writer.warn({
110
+ message: this.httpRequestErrorMessage('Ping'),
111
+ err: error,
112
+ args: {
113
+ connection_id: this.connectionId,
114
+ query_id,
115
+ },
116
+ });
117
+ }
128
118
  return {
129
119
  success: false,
130
120
  error: error, // should NOT be propagated to the user
@@ -135,6 +125,7 @@ class NodeBaseConnection {
135
125
  }
136
126
  }
137
127
  async query(params) {
128
+ const { log_writer, log_level } = this.params;
138
129
  const query_id = this.getQueryId(params.query_id);
139
130
  const clickhouse_settings = (0, client_common_1.withHttpSettings)(params.clickhouse_settings, this.params.compression.decompress_response);
140
131
  const searchParams = (0, client_common_1.toSearchParams)({
@@ -157,6 +148,9 @@ class NodeBaseConnection {
157
148
  enable_response_compression: enableResponseCompression,
158
149
  headers: this.buildRequestHeaders(params),
159
150
  query: params.query,
151
+ query_id,
152
+ log_writer,
153
+ log_level,
160
154
  }, 'Query');
161
155
  return {
162
156
  stream,
@@ -185,6 +179,7 @@ class NodeBaseConnection {
185
179
  }
186
180
  }
187
181
  async insert(params) {
182
+ const { log_writer, log_level } = this.params;
188
183
  const query_id = this.getQueryId(params.query_id);
189
184
  const searchParams = (0, client_common_1.toSearchParams)({
190
185
  database: this.params.database,
@@ -206,8 +201,16 @@ class NodeBaseConnection {
206
201
  parse_summary: true,
207
202
  headers: this.buildRequestHeaders(params),
208
203
  query: params.query,
204
+ query_id,
205
+ log_writer,
206
+ log_level,
209
207
  }, 'Insert');
210
- await (0, stream_2.drainStream)(stream);
208
+ await (0, stream_2.drainStreamInternal)({
209
+ op: 'Insert',
210
+ log_writer,
211
+ query_id,
212
+ log_level,
213
+ }, stream);
211
214
  return { query_id, summary, response_headers, http_status_code };
212
215
  }
213
216
  catch (err) {
@@ -229,18 +232,71 @@ class NodeBaseConnection {
229
232
  }
230
233
  }
231
234
  async exec(params) {
235
+ const query_id = this.getQueryId(params.query_id);
232
236
  return this.runExec({
233
237
  ...params,
238
+ query_id,
234
239
  op: 'Exec',
235
240
  });
236
241
  }
237
242
  async command(params) {
238
- const { stream, query_id, summary, response_headers } = await this.runExec({
243
+ const { log_writer, log_level } = this.params;
244
+ const query_id = this.getQueryId(params.query_id);
245
+ const commandStartTime = Date.now();
246
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
247
+ log_writer.trace({
248
+ message: 'Command: operation started',
249
+ args: {
250
+ operation: 'Command',
251
+ connection_id: this.connectionId,
252
+ query_id,
253
+ },
254
+ });
255
+ }
256
+ const { stream, summary, response_headers } = await this.runExec({
239
257
  ...params,
258
+ query_id,
240
259
  op: 'Command',
241
260
  });
261
+ const runExecDuration = Date.now() - commandStartTime;
262
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
263
+ log_writer.trace({
264
+ message: 'Command: runExec completed, starting stream drain',
265
+ args: {
266
+ operation: 'Command',
267
+ connection_id: this.connectionId,
268
+ query_id,
269
+ runExec_duration_ms: runExecDuration,
270
+ stream_state: {
271
+ readable: stream.readable,
272
+ readableEnded: stream.readableEnded,
273
+ readableLength: stream.readableLength,
274
+ },
275
+ },
276
+ });
277
+ }
242
278
  // ignore the response stream and release the socket immediately
243
- await (0, stream_2.drainStream)(stream);
279
+ const drainStartTime = Date.now();
280
+ await (0, stream_2.drainStreamInternal)({
281
+ op: 'Command',
282
+ log_writer,
283
+ query_id,
284
+ log_level,
285
+ }, stream);
286
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
287
+ const drainDuration = Date.now() - drainStartTime;
288
+ const totalDuration = Date.now() - commandStartTime;
289
+ log_writer.trace({
290
+ message: 'Command: operation completed',
291
+ args: {
292
+ operation: 'Command',
293
+ connection_id: this.connectionId,
294
+ query_id,
295
+ drain_duration_ms: drainDuration,
296
+ total_duration_ms: totalDuration,
297
+ },
298
+ });
299
+ }
244
300
  return { query_id, summary, response_headers };
245
301
  }
246
302
  async close() {
@@ -303,37 +359,21 @@ class NodeBaseConnection {
303
359
  },
304
360
  };
305
361
  }
306
- logResponse(op, request, params, response, startTimestamp) {
307
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
308
- const { authorization, host, ...headers } = request.getHeaders();
309
- const duration = Date.now() - startTimestamp;
310
- this.params.log_writer.debug({
311
- module: 'HTTP Adapter',
312
- message: `${op}: got a response from ClickHouse`,
313
- args: {
314
- request_method: params.method,
315
- request_path: params.url.pathname,
316
- request_params: params.url.search,
317
- request_headers: headers,
318
- response_status: response.statusCode,
319
- response_headers: response.headers,
320
- response_time_ms: duration,
321
- },
322
- });
323
- }
324
- logRequestError({ op, err, query_id, query_params, search_params, extra_args, }) {
325
- this.logger.error({
326
- message: this.httpRequestErrorMessage(op),
327
- err: err,
328
- args: {
329
- query: query_params.query,
330
- search_params: search_params?.toString() ?? '',
331
- with_abort_signal: query_params.abort_signal !== undefined,
332
- session_id: query_params.session_id,
333
- query_id: query_id,
334
- ...extra_args,
335
- },
336
- });
362
+ logRequestError({ op, err, query_id, query_params, extra_args, }) {
363
+ if (this.params.log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
364
+ this.params.log_writer.error({
365
+ message: this.httpRequestErrorMessage(op),
366
+ err: err,
367
+ args: {
368
+ operation: op,
369
+ connection_id: this.connectionId,
370
+ query_id,
371
+ with_abort_signal: query_params.abort_signal !== undefined,
372
+ session_id: query_params.session_id,
373
+ ...extra_args,
374
+ },
375
+ });
376
+ }
337
377
  }
338
378
  httpRequestErrorMessage(op) {
339
379
  return `${op}: HTTP request error.`;
@@ -345,18 +385,23 @@ class NodeBaseConnection {
345
385
  return this.jsonHandling.parse(summaryHeader);
346
386
  }
347
387
  catch (err) {
348
- this.logger.error({
349
- message: `${op}: failed to parse X-ClickHouse-Summary header.`,
350
- args: {
351
- 'X-ClickHouse-Summary': summaryHeader,
352
- },
353
- err: err,
354
- });
388
+ if (this.params.log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
389
+ this.params.log_writer.error({
390
+ message: `${op}: failed to parse X-ClickHouse-Summary header.`,
391
+ args: {
392
+ operation: op,
393
+ connection_id: this.connectionId,
394
+ 'X-ClickHouse-Summary': summaryHeader,
395
+ },
396
+ err: err,
397
+ });
398
+ }
355
399
  }
356
400
  }
357
401
  }
358
402
  async runExec(params) {
359
- const query_id = this.getQueryId(params.query_id);
403
+ const { log_writer, log_level } = this.params;
404
+ const query_id = params.query_id;
360
405
  const sendQueryInParams = params.values !== undefined;
361
406
  const clickhouse_settings = (0, client_common_1.withHttpSettings)(params.clickhouse_settings, this.params.compression.decompress_response);
362
407
  const toSearchParamsOptions = {
@@ -391,6 +436,9 @@ class NodeBaseConnection {
391
436
  ignore_error_response: ignoreErrorResponse,
392
437
  headers: this.buildRequestHeaders(params),
393
438
  query: params.query,
439
+ query_id,
440
+ log_writer,
441
+ log_level,
394
442
  }, params.op);
395
443
  return {
396
444
  stream,
@@ -422,13 +470,15 @@ class NodeBaseConnection {
422
470
  // allows the event loop to process the idle socket timers, if the CPU load is high
423
471
  // otherwise, we can occasionally get an expired socket, see https://github.com/ClickHouse/clickhouse-js/issues/294
424
472
  await (0, client_common_1.sleep)(0);
473
+ const { log_writer, query_id, log_level } = params;
425
474
  const currentStackTrace = this.params.capture_enhanced_stack_trace
426
475
  ? (0, client_common_1.getCurrentStackTrace)()
427
476
  : undefined;
428
- const logger = this.logger;
477
+ const requestTimeout = this.params.request_timeout;
429
478
  return new Promise((resolve, reject) => {
430
479
  const start = Date.now();
431
480
  const request = this.createClientRequest(params);
481
+ const request_id = this.getNewRequestId();
432
482
  function onError(e) {
433
483
  removeRequestListeners();
434
484
  const err = (0, client_common_1.enhanceStackTrace)(e, currentStackTrace);
@@ -436,14 +486,30 @@ class NodeBaseConnection {
436
486
  }
437
487
  let responseStream;
438
488
  const onResponse = async (_response) => {
439
- this.logResponse(op, request, params, _response, start);
489
+ if (this.params.log_level <= client_common_1.ClickHouseLogLevel.DEBUG) {
490
+ const duration = Date.now() - start;
491
+ this.params.log_writer.debug({
492
+ module: 'HTTP Adapter',
493
+ message: `${op}: got a response from ClickHouse`,
494
+ args: {
495
+ operation: op,
496
+ connection_id: this.connectionId,
497
+ query_id,
498
+ request_id,
499
+ request_method: params.method,
500
+ request_path: params.url.pathname,
501
+ response_status: _response.statusCode,
502
+ response_time_ms: duration,
503
+ },
504
+ });
505
+ }
440
506
  const tryDecompressResponseStream = params.try_decompress_response_stream ?? true;
441
507
  const ignoreErrorResponse = params.ignore_error_response ?? false;
442
508
  // even if the stream decompression is disabled, we have to decompress it in case of an error
443
509
  const isFailedResponse = !(0, client_common_1.isSuccessfulResponse)(_response.statusCode);
444
510
  if (tryDecompressResponseStream ||
445
511
  (isFailedResponse && !ignoreErrorResponse)) {
446
- const decompressionResult = (0, compression_1.decompressResponse)(_response, this.logger);
512
+ const decompressionResult = (0, compression_1.decompressResponse)(_response, log_writer, log_level);
447
513
  if ((0, compression_1.isDecompressionError)(decompressionResult)) {
448
514
  const err = (0, client_common_1.enhanceStackTrace)(decompressionResult.error, currentStackTrace);
449
515
  return reject(err);
@@ -453,6 +519,24 @@ class NodeBaseConnection {
453
519
  else {
454
520
  responseStream = _response;
455
521
  }
522
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
523
+ log_writer.trace({
524
+ message: `${op}: response stream created`,
525
+ args: {
526
+ operation: op,
527
+ connection_id: this.connectionId,
528
+ query_id,
529
+ request_id,
530
+ stream_state: {
531
+ readable: responseStream.readable,
532
+ readableEnded: responseStream.readableEnded,
533
+ readableLength: responseStream.readableLength,
534
+ },
535
+ is_failed_response: isFailedResponse,
536
+ will_decompress: tryDecompressResponseStream,
537
+ },
538
+ });
539
+ }
456
540
  if (isFailedResponse && !ignoreErrorResponse) {
457
541
  try {
458
542
  const errorMessage = await (0, utils_1.getAsText)(responseStream);
@@ -525,96 +609,226 @@ class NodeBaseConnection {
525
609
  // It is the first time we've encountered this socket,
526
610
  // so it doesn't have the idle timeout handler attached to it
527
611
  if (socketInfo === undefined) {
528
- const socketId = crypto_1.default.randomUUID();
529
- this.logger.trace({
530
- message: `Using a fresh socket ${socketId}, setting up a new 'free' listener`,
531
- });
532
- this.knownSockets.set(socket, {
533
- id: socketId,
612
+ const socket_id = this.getNewSocketId();
613
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
614
+ log_writer.trace({
615
+ message: `${op}: using a fresh socket, setting up a new 'free' listener`,
616
+ args: {
617
+ operation: op,
618
+ connection_id: this.connectionId,
619
+ query_id,
620
+ request_id,
621
+ socket_id,
622
+ },
623
+ });
624
+ }
625
+ const newSocketInfo = {
626
+ id: socket_id,
534
627
  idle_timeout_handle: undefined,
535
- });
628
+ usage_count: 1,
629
+ };
630
+ this.knownSockets.set(socket, newSocketInfo);
536
631
  // When the request is complete and the socket is released,
537
632
  // make sure that the socket is removed after `idleSocketTTL`.
538
633
  socket.on('free', () => {
539
- this.logger.trace({
540
- message: `Socket ${socketId} was released`,
541
- });
634
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
635
+ log_writer.trace({
636
+ message: `${op}: socket was released`,
637
+ args: {
638
+ operation: op,
639
+ connection_id: this.connectionId,
640
+ query_id,
641
+ request_id,
642
+ socket_id,
643
+ },
644
+ });
645
+ }
542
646
  // Avoiding the built-in socket.timeout() method usage here,
543
647
  // as we don't want to clash with the actual request timeout.
544
648
  const idleTimeoutHandle = setTimeout(() => {
545
- this.logger.trace({
546
- message: `Removing socket ${socketId} after ${this.idleSocketTTL} ms of idle`,
547
- });
649
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
650
+ log_writer.trace({
651
+ message: `${op}: removing idle socket`,
652
+ args: {
653
+ operation: op,
654
+ connection_id: this.connectionId,
655
+ query_id,
656
+ request_id,
657
+ socket_id,
658
+ idle_socket_ttl_ms: this.idleSocketTTL,
659
+ },
660
+ });
661
+ }
548
662
  this.knownSockets.delete(socket);
549
663
  socket.destroy();
550
664
  }, this.idleSocketTTL).unref();
551
- this.knownSockets.set(socket, {
552
- id: socketId,
553
- idle_timeout_handle: idleTimeoutHandle,
554
- });
665
+ newSocketInfo.idle_timeout_handle = idleTimeoutHandle;
555
666
  });
556
- const cleanup = () => {
667
+ const cleanup = (eventName) => () => {
557
668
  const maybeSocketInfo = this.knownSockets.get(socket);
558
669
  // clean up a possibly dangling idle timeout handle (preventing leaks)
559
670
  if (maybeSocketInfo?.idle_timeout_handle) {
560
671
  clearTimeout(maybeSocketInfo.idle_timeout_handle);
561
672
  }
562
- this.logger.trace({
563
- message: `Socket ${socketId} was closed or ended, 'free' listener removed`,
564
- });
565
- if (responseStream && !responseStream.readableEnded) {
566
- this.logger.warn({
567
- message: `${op}: socket was closed or ended before the response was fully read. ` +
568
- 'This can potentially result in an uncaught ECONNRESET error! ' +
569
- 'Consider fully consuming, draining, or destroying the response stream.',
673
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
674
+ log_writer.trace({
675
+ message: `${op}: received '${eventName}' event, 'free' listener removed`,
570
676
  args: {
571
- query: params.query,
572
- query_id: params.url.searchParams.get('query_id') ?? 'unknown',
677
+ operation: op,
678
+ connection_id: this.connectionId,
679
+ query_id,
680
+ request_id,
681
+ socket_id,
682
+ event: eventName,
573
683
  },
574
684
  });
575
685
  }
686
+ if (log_level <= client_common_1.ClickHouseLogLevel.WARN) {
687
+ if (responseStream && !responseStream.readableEnded) {
688
+ log_writer.warn({
689
+ message: `${op}: socket was closed or ended before the response was fully read. ` +
690
+ 'This can potentially result in an uncaught ECONNRESET error! ' +
691
+ 'Consider fully consuming, draining, or destroying the response stream.',
692
+ args: {
693
+ operation: op,
694
+ connection_id: this.connectionId,
695
+ query_id,
696
+ request_id,
697
+ socket_id,
698
+ event: eventName,
699
+ },
700
+ });
701
+ }
702
+ }
576
703
  };
577
- socket.once('end', cleanup);
578
- socket.once('close', cleanup);
704
+ socket.once('end', cleanup('end'));
705
+ socket.once('close', cleanup('close'));
579
706
  }
580
707
  else {
581
708
  clearTimeout(socketInfo.idle_timeout_handle);
582
- this.logger.trace({
583
- message: `Reusing socket ${socketInfo.id}`,
584
- });
585
- this.knownSockets.set(socket, {
586
- ...socketInfo,
587
- idle_timeout_handle: undefined,
588
- });
709
+ socketInfo.idle_timeout_handle = undefined;
710
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
711
+ log_writer.trace({
712
+ message: `${op}: reusing socket`,
713
+ args: {
714
+ operation: op,
715
+ connection_id: this.connectionId,
716
+ query_id,
717
+ request_id,
718
+ socket_id: socketInfo.id,
719
+ usage_count: socketInfo.usage_count,
720
+ },
721
+ });
722
+ }
723
+ socketInfo.usage_count++;
589
724
  }
590
725
  }
591
726
  }
592
727
  catch (e) {
593
- logger.error({
594
- message: 'An error occurred while housekeeping the idle sockets',
595
- err: e,
596
- });
728
+ if (log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
729
+ log_writer.error({
730
+ message: `${op}: an error occurred while housekeeping the idle sockets`,
731
+ err: e,
732
+ args: {
733
+ operation: op,
734
+ connection_id: this.connectionId,
735
+ query_id,
736
+ request_id,
737
+ },
738
+ });
739
+ }
597
740
  }
598
741
  // Socket is "prepared" with idle handlers, continue with our request
599
742
  pipeStream();
600
743
  // This is for request timeout only. Surprisingly, it is not always enough to set in the HTTP request.
601
744
  // The socket won't be destroyed, and it will be returned to the pool.
745
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
746
+ const socketInfo = this.knownSockets.get(socket);
747
+ if (socketInfo) {
748
+ log_writer.trace({
749
+ message: `${op}: setting up request timeout`,
750
+ args: {
751
+ operation: op,
752
+ connection_id: this.connectionId,
753
+ query_id,
754
+ request_id,
755
+ socket_id: socketInfo.id,
756
+ timeout_ms: requestTimeout,
757
+ },
758
+ });
759
+ }
760
+ else {
761
+ log_writer.trace({
762
+ message: `${op}: setting up request timeout on a socket`,
763
+ args: {
764
+ operation: op,
765
+ connection_id: this.connectionId,
766
+ query_id,
767
+ request_id,
768
+ timeout_ms: requestTimeout,
769
+ },
770
+ });
771
+ }
772
+ }
602
773
  socket.setTimeout(this.params.request_timeout, onTimeout);
603
774
  };
604
- function onTimeout() {
605
- const err = (0, client_common_1.enhanceStackTrace)(new Error('Timeout error.'), currentStackTrace);
775
+ const onTimeout = () => {
606
776
  removeRequestListeners();
777
+ if (log_level <= client_common_1.ClickHouseLogLevel.TRACE) {
778
+ const socket = request.socket;
779
+ const maybeSocketInfo = socket
780
+ ? this.knownSockets.get(socket)
781
+ : undefined;
782
+ const socketState = request.socket
783
+ ? {
784
+ connecting: request.socket.connecting,
785
+ pending: request.socket.pending,
786
+ destroyed: request.socket.destroyed,
787
+ readyState: request.socket.readyState,
788
+ }
789
+ : undefined;
790
+ const responseStreamState = responseStream
791
+ ? {
792
+ readable: responseStream.readable,
793
+ readableEnded: responseStream.readableEnded,
794
+ readableLength: responseStream.readableLength,
795
+ }
796
+ : undefined;
797
+ log_writer.trace({
798
+ message: `${op}: timeout occurred`,
799
+ args: {
800
+ operation: op,
801
+ connection_id: this.connectionId,
802
+ query_id,
803
+ request_id,
804
+ socket_id: maybeSocketInfo?.id,
805
+ timeout_ms: requestTimeout,
806
+ socket_state: socketState,
807
+ response_stream_state: responseStreamState,
808
+ has_response_stream: responseStream !== undefined,
809
+ },
810
+ });
811
+ }
812
+ const err = (0, client_common_1.enhanceStackTrace)(new Error('Timeout error.'), currentStackTrace);
607
813
  try {
608
814
  request.destroy();
609
815
  }
610
816
  catch (e) {
611
- logger.error({
612
- message: 'An error occurred while destroying the request',
613
- err: e,
614
- });
817
+ if (log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
818
+ log_writer.error({
819
+ message: `${op}: An error occurred while destroying the request`,
820
+ err: e,
821
+ args: {
822
+ operation: op,
823
+ connection_id: this.connectionId,
824
+ query_id,
825
+ request_id,
826
+ },
827
+ });
828
+ }
615
829
  }
616
830
  reject(err);
617
- }
831
+ };
618
832
  function removeRequestListeners() {
619
833
  if (request.socket !== null) {
620
834
  request.socket.setTimeout(0); // reset previously set timeout
@@ -642,10 +856,18 @@ class NodeBaseConnection {
642
856
  return request.end();
643
857
  }
644
858
  catch (e) {
645
- this.logger.error({
646
- message: 'An error occurred while ending the request without body',
647
- err: e,
648
- });
859
+ if (log_level <= client_common_1.ClickHouseLogLevel.ERROR) {
860
+ log_writer.error({
861
+ message: `${op}: an error occurred while ending the request without body`,
862
+ err: e,
863
+ args: {
864
+ operation: op,
865
+ connection_id: this.connectionId,
866
+ query_id,
867
+ request_id,
868
+ },
869
+ });
870
+ }
649
871
  }
650
872
  }
651
873
  });