Repository navigation
Expand file tree
/
Copy pathinsert_streaming_backpressure_simple.ts
More file actions
135 lines (112 loc) · 3.12 KB
/
Copy pathinsert_streaming_backpressure_simple.ts
File metadata and controls
135 lines (112 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { createClient } from "@clickhouse/client";
import * as Stream from "node:stream";
interface DataRow {
id: number;
name: string;
value: number;
}
class SimpleBackpressureStream extends Stream.Readable {
#currentId = 1;
#maxRecords = 0;
#intervalId: NodeJS.Timeout | null = null;
#isPaused = false;
constructor(maxRecords: number) {
super({ objectMode: true, highWaterMark: 5 });
this.#maxRecords = maxRecords;
}
_read() {
if (this.#isPaused) {
console.log("Backpressure relieved - resuming data production");
this.#isPaused = false;
this.#startProducing();
}
}
#startProducing() {
if (this.#intervalId || this.#currentId > this.#maxRecords) {
return;
}
this.#intervalId = setInterval(() => {
if (this.#currentId > this.#maxRecords) {
console.log("All data produced, ending stream");
this.push(null); // End the stream
this.#stopProducing();
return;
}
const data: DataRow = {
id: this.#currentId++,
name: `Name_${this.#currentId - 1}`,
value: Math.random() * 1000,
};
const canContinue = this.push(data);
if (!canContinue) {
console.log("Backpressure detected - pausing data production");
this.#isPaused = true;
this.#stopProducing();
} else if (this.#currentId % 500 === 0) {
console.log(`Produced ${this.#currentId - 1} records`);
}
}, 1);
}
#stopProducing() {
if (this.#intervalId) {
clearInterval(this.#intervalId);
this.#intervalId = null;
}
}
start() {
this.#startProducing();
}
_destroy(error: Error | null, callback: (error?: Error | null) => void) {
this.#stopProducing();
callback(error);
}
}
const tableName = "simple_streaming_demo";
const client = createClient();
// Setup table
await client.command({
query: `DROP TABLE IF EXISTS ${tableName}`,
});
await client.command({
query: `
CREATE TABLE ${tableName}
(
id UInt32,
name String,
value Float64
)
ENGINE = MergeTree()
ORDER BY id
`,
});
const maxRecords = 10000;
console.log("Creating backpressure-aware data stream...");
const dataStream = new SimpleBackpressureStream(maxRecords);
try {
console.log("Starting streaming insert with backpressure demonstration...");
const insertPromise = client.insert({
table: tableName,
values: dataStream,
format: "JSONEachRow",
clickhouse_settings: {
// Use async inserts to handle streaming data more efficiently
async_insert: 1,
wait_for_async_insert: 1,
async_insert_max_data_size: "10485760", // 10MB
async_insert_busy_timeout_ms: 1000,
},
});
setTimeout(() => dataStream.start(), 100);
await insertPromise;
console.log("Insert completed successfully!");
const result = await client.query({
query: `SELECT count() as total FROM ${tableName}`,
format: "JSONEachRow",
});
const [{ total }] = await result.json<{ total: string }>();
console.log(`Total records inserted: ${total}`);
} catch (error) {
console.error("Insert failed:", error);
} finally {
await client.close();
}