@clickhouse/client 1.18.4 → 1.18.5-head.09b78f0.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/dist/config.d.ts +17 -0
- 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 +9 -0
- package/dist/connection/node_base_connection.js.map +1 -1
- package/dist/connection/node_custom_agent_connection.js +3 -0
- package/dist/connection/node_custom_agent_connection.js.map +1 -1
- package/dist/connection/node_http_connection.js +3 -0
- package/dist/connection/node_http_connection.js.map +1 -1
- package/dist/connection/node_https_connection.js +3 -0
- package/dist/connection/node_https_connection.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 +6 -2
- package/skills/clickhouse-js-node-coding/SKILL.md +146 -0
- package/skills/clickhouse-js-node-coding/evals/evals.json +103 -0
- package/skills/clickhouse-js-node-coding/reference/async-insert.md +103 -0
- package/skills/clickhouse-js-node-coding/reference/client-configuration.md +159 -0
- package/skills/clickhouse-js-node-coding/reference/custom-json.md +149 -0
- package/skills/clickhouse-js-node-coding/reference/data-types.md +169 -0
- package/skills/clickhouse-js-node-coding/reference/insert-columns.md +113 -0
- package/skills/clickhouse-js-node-coding/reference/insert-formats.md +145 -0
- package/skills/clickhouse-js-node-coding/reference/insert-values.md +141 -0
- package/skills/clickhouse-js-node-coding/reference/ping.md +120 -0
- package/skills/clickhouse-js-node-coding/reference/query-parameters.md +152 -0
- package/skills/clickhouse-js-node-coding/reference/select-formats.md +111 -0
- package/skills/clickhouse-js-node-coding/reference/sessions.md +152 -0
- package/skills/clickhouse-js-node-troubleshooting/reference/socket-hangup.md +15 -3
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Sessions and Temporary Tables
|
|
2
|
+
|
|
3
|
+
> **Applies to:** all versions. `session_id` is a server-level concept; the
|
|
4
|
+
> client just forwards it on every request that names it.
|
|
5
|
+
|
|
6
|
+
Backing examples:
|
|
7
|
+
[`examples/node/coding/session_id_and_temporary_tables.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/session_id_and_temporary_tables.ts),
|
|
8
|
+
[`examples/node/coding/session_level_commands.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/session_level_commands.ts).
|
|
9
|
+
|
|
10
|
+
## When you need a session
|
|
11
|
+
|
|
12
|
+
Use a `session_id` whenever multiple calls must share **server-side state**:
|
|
13
|
+
|
|
14
|
+
- `CREATE TEMPORARY TABLE` (the table only exists within its session).
|
|
15
|
+
- `SET <setting> = <value>` to apply for subsequent queries on the same
|
|
16
|
+
session.
|
|
17
|
+
- Any other server feature scoped per session (e.g., session-scoped
|
|
18
|
+
variables in newer ClickHouse versions).
|
|
19
|
+
|
|
20
|
+
## ⚠️ `session_id` and concurrency
|
|
21
|
+
|
|
22
|
+
ClickHouse **rejects concurrent queries within the same session** — if two
|
|
23
|
+
requests arrive at the server at the same time sharing the same `session_id`,
|
|
24
|
+
the second one gets an error like
|
|
25
|
+
`"Session is locked by a concurrent client"`. This has two practical
|
|
26
|
+
implications:
|
|
27
|
+
|
|
28
|
+
1. **Do not set `session_id` on a global / module-static client** that handles
|
|
29
|
+
concurrent requests (e.g., an Express app's shared client). Every
|
|
30
|
+
in-flight request would share the same session and collide under load.
|
|
31
|
+
2. **If you do set `session_id` on a client**, restrict its concurrency:
|
|
32
|
+
set `max_open_connections: 1` so at most one request is in flight at a
|
|
33
|
+
time, turning the pool into a serial queue. This is fine for a
|
|
34
|
+
dedicated per-workflow client but wrong for a shared application client.
|
|
35
|
+
|
|
36
|
+
The right pattern for application code: create a **short-lived client** (or
|
|
37
|
+
use per-request `session_id`) scoped to a single logical workflow, not to
|
|
38
|
+
the entire process.
|
|
39
|
+
|
|
40
|
+
## Per-client `session_id`
|
|
41
|
+
|
|
42
|
+
Appropriate when **one client handles exactly one sequential workflow** (a
|
|
43
|
+
script, a background job, a single user's session that you've already
|
|
44
|
+
serialized).
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { createClient } from '@clickhouse/client'
|
|
48
|
+
import * as crypto from 'node:crypto'
|
|
49
|
+
|
|
50
|
+
const client = createClient({
|
|
51
|
+
session_id: crypto.randomUUID(),
|
|
52
|
+
max_open_connections: 1, // prevent concurrent-session errors
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
await client.command({
|
|
56
|
+
query: 'CREATE TEMPORARY TABLE temporary_example (i Int32)',
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
await client.insert({
|
|
60
|
+
table: 'temporary_example',
|
|
61
|
+
values: [{ i: 42 }, { i: 144 }],
|
|
62
|
+
format: 'JSONEachRow',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const rs = await client.query({
|
|
66
|
+
query: 'SELECT * FROM temporary_example',
|
|
67
|
+
format: 'JSONEachRow',
|
|
68
|
+
})
|
|
69
|
+
console.info(await rs.json())
|
|
70
|
+
await client.close()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Session-level `SET` commands
|
|
74
|
+
|
|
75
|
+
`SET` only persists within a session. With `session_id` defined on the
|
|
76
|
+
client, every subsequent call inherits the change.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { createClient } from '@clickhouse/client'
|
|
80
|
+
import * as crypto from 'node:crypto'
|
|
81
|
+
|
|
82
|
+
const client = createClient({
|
|
83
|
+
session_id: crypto.randomUUID(),
|
|
84
|
+
max_open_connections: 1, // prevent concurrent-session errors
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
await client.command({
|
|
88
|
+
query: 'SET output_format_json_quote_64bit_integers = 0',
|
|
89
|
+
clickhouse_settings: { wait_end_of_query: 1 }, // ack before next call
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const rs1 = await client.query({
|
|
93
|
+
query: 'SELECT toInt64(42)',
|
|
94
|
+
format: 'JSONEachRow',
|
|
95
|
+
})
|
|
96
|
+
// → 64-bit integers come back as numbers in this query
|
|
97
|
+
|
|
98
|
+
await client.command({
|
|
99
|
+
query: 'SET output_format_json_quote_64bit_integers = 1',
|
|
100
|
+
clickhouse_settings: { wait_end_of_query: 1 },
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const rs2 = await client.query({
|
|
104
|
+
query: 'SELECT toInt64(144)',
|
|
105
|
+
format: 'JSONEachRow',
|
|
106
|
+
})
|
|
107
|
+
// → 64-bit integers come back as strings again
|
|
108
|
+
|
|
109
|
+
await client.close()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> **`wait_end_of_query: 1` matters here.** Without it, a `SET` on one
|
|
113
|
+
> connection in the pool may not yet be applied when the next query lands
|
|
114
|
+
> on the same socket.
|
|
115
|
+
|
|
116
|
+
## Per-request `session_id`
|
|
117
|
+
|
|
118
|
+
You can also pass `session_id` on a single `query()` / `insert()` /
|
|
119
|
+
`command()` call to override (or set) it for that one request.
|
|
120
|
+
|
|
121
|
+
## ⚠️ Sessions and load balancers / ClickHouse Cloud
|
|
122
|
+
|
|
123
|
+
Sessions are bound to a **specific ClickHouse node**. If a load balancer in
|
|
124
|
+
front of ClickHouse routes consecutive requests to different nodes, the
|
|
125
|
+
temporary table / `SET` won't be visible — you'll get
|
|
126
|
+
`UNKNOWN_TABLE` / surprising results.
|
|
127
|
+
|
|
128
|
+
Mitigations:
|
|
129
|
+
|
|
130
|
+
- Talk to a single node directly.
|
|
131
|
+
- For ClickHouse Cloud, use [replica-aware
|
|
132
|
+
routing](https://clickhouse.com/docs/manage/replica-aware-routing).
|
|
133
|
+
- Avoid sessions for cross-node workflows; persist intermediate state in a
|
|
134
|
+
regular (non-temporary) table instead.
|
|
135
|
+
|
|
136
|
+
## Common pitfalls
|
|
137
|
+
|
|
138
|
+
- **Forgetting `session_id` and being surprised that
|
|
139
|
+
`CREATE TEMPORARY TABLE` "disappears."** Without a session, every request
|
|
140
|
+
may land on a different connection / server context.
|
|
141
|
+
- **Setting `session_id` on a shared application client.** Under concurrent
|
|
142
|
+
load, two in-flight requests will share the same session and one will fail
|
|
143
|
+
with `"Session is locked by a concurrent client"`. Use per-request
|
|
144
|
+
`session_id` or a dedicated short-lived client instead.
|
|
145
|
+
- **Reusing the same `session_id` across unrelated workflows.** A second
|
|
146
|
+
session-using consumer will trip over your temporary tables and `SET`
|
|
147
|
+
values. Generate a fresh UUID per logical session.
|
|
148
|
+
- **Leaving session state pinned for the lifetime of the process.** If
|
|
149
|
+
long-lived clients accumulate `SET` / temp-table state, consider creating
|
|
150
|
+
a short-lived sub-client with its own `session_id` for the unit of work.
|
|
151
|
+
- **Skipping `wait_end_of_query: 1` on `SET`** — race conditions between
|
|
152
|
+
`SET` and the next query can show up under load.
|
|
@@ -113,7 +113,7 @@ const client = createClient({
|
|
|
113
113
|
|
|
114
114
|
**Node.js defaults to a total received HTTP header limit of approximately 16 KB (this can be increased via the `--max-http-header-size` CLI flag[^max-header-size]).** ClickHouse sends a new progress header with each interval (~200 bytes), and after ~75 progress headers accumulate, Node.js will throw an exception and terminate the request unless that limit is raised.
|
|
115
115
|
|
|
116
|
-
[^max-header-size]:
|
|
116
|
+
[^max-header-size]: Since `>= 1.18.5`, the ClickHouse JS client also forwards a per-request limit via the `max_response_headers_size` (bytes) option on `createClient` (Node.js only — see the example below). On older versions, the practical workarounds are the `--max-http-header-size` CLI flag / `NODE_OPTIONS` (process-wide) or supplying a custom `http.Agent` configured with `maxHeaderSize`.
|
|
117
117
|
|
|
118
118
|
**Maximum safe query duration formula:**
|
|
119
119
|
|
|
@@ -133,11 +133,23 @@ Max duration (seconds) ≈ http_headers_progress_interval_ms × 75 ÷ 1000
|
|
|
133
133
|
|
|
134
134
|
If you need a longer max safe duration without lengthening the progress interval, raise Node's HTTP header limit. For example, increasing it from the default 16 KB to **64 KB** quadruples the max safe duration (≈300 progress headers instead of ≈75).
|
|
135
135
|
|
|
136
|
+
```ts
|
|
137
|
+
// Option 1 (recommended, since `>= 1.18.5`) — per-client, no process-wide flag needed
|
|
138
|
+
const client = createClient({
|
|
139
|
+
request_timeout: 400_000,
|
|
140
|
+
max_response_headers_size: 65536, // 64 KB; lifts the per-request header cap
|
|
141
|
+
clickhouse_settings: {
|
|
142
|
+
send_progress_in_http_headers: 1,
|
|
143
|
+
http_headers_progress_interval_ms: '110000',
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
```
|
|
147
|
+
|
|
136
148
|
```bash
|
|
137
|
-
# Option
|
|
149
|
+
# Option 2 — CLI flag when launching your app (process-wide; older client versions)
|
|
138
150
|
node --max-http-header-size=65536 app.js
|
|
139
151
|
|
|
140
|
-
# Option
|
|
152
|
+
# Option 3 — environment variable (works with any Node entry point, including npm/ts-node)
|
|
141
153
|
NODE_OPTIONS="--max-http-header-size=65536" node app.js
|
|
142
154
|
```
|
|
143
155
|
|