@transitive-sdk/clickhouse 0.4.3 → 0.5.0
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/index.js +39 -18
- package/package.json +1 -1
- package/test/clickhouse.test.js +1 -2
package/index.js
CHANGED
|
@@ -47,6 +47,8 @@ class ClickHouse {
|
|
|
47
47
|
|
|
48
48
|
mqttHistoryTable = null; // name of the table used for MQTT history, if used
|
|
49
49
|
topics = {}; // list of topics registered for storage, as object for de-duplication
|
|
50
|
+
rowCache = {}; // cache of rows awaiting insertion, by table
|
|
51
|
+
insertionInterval = null; // the actual interval
|
|
50
52
|
|
|
51
53
|
/** Create the client, connecting to Clickhouse */
|
|
52
54
|
async init({ url, dbName, user, password } = {}) {
|
|
@@ -142,7 +144,7 @@ class ClickHouse {
|
|
|
142
144
|
* @param {string} orgId - organization ID to add to each row
|
|
143
145
|
* @param {string} deviceId - device ID to add to each row
|
|
144
146
|
*/
|
|
145
|
-
|
|
147
|
+
insert(tableName, rows, orgId, deviceId) {
|
|
146
148
|
// assert that orgId and deviceId are provided
|
|
147
149
|
if (!orgId || !deviceId) {
|
|
148
150
|
throw new Error('Both orgId and deviceId must be provided for multi-tenant insert');
|
|
@@ -155,18 +157,15 @@ class ClickHouse {
|
|
|
155
157
|
DeviceId: deviceId
|
|
156
158
|
}));
|
|
157
159
|
|
|
158
|
-
return
|
|
159
|
-
table: tableName,
|
|
160
|
-
values: rowsWithIds,
|
|
161
|
-
format: 'JSONEachRow'
|
|
162
|
-
});
|
|
160
|
+
return this.addToCache(tableName, rowsWithIds);
|
|
163
161
|
}
|
|
164
162
|
|
|
165
163
|
/* Enable history recording. Ensure the mqtt_history table exists with the
|
|
166
164
|
* correct schema, set dataCache, and subscribe to changes.
|
|
167
165
|
* @param {object} options = {dataCache, tableName, ttlDays}
|
|
166
|
+
* @param {number} interval = ms interval between batch insertions
|
|
168
167
|
*/
|
|
169
|
-
async enableHistory(options) {
|
|
168
|
+
async enableHistory(options, interval = 10_000) {
|
|
170
169
|
const { dataCache, tableName = 'mqtt_history' } = options;
|
|
171
170
|
|
|
172
171
|
if (this.mqttHistoryTable != tableName) {
|
|
@@ -241,7 +240,7 @@ class ClickHouse {
|
|
|
241
240
|
// it matches any of the registered topics (this avoid duplicate triggers),
|
|
242
241
|
// then store to ClickHouse with current timestamp.
|
|
243
242
|
dataCache.subscribe((changes) => {
|
|
244
|
-
_.forEach(changes,
|
|
243
|
+
_.forEach(changes, (value, topic) => {
|
|
245
244
|
|
|
246
245
|
const matched =
|
|
247
246
|
_.some(this.topics, (_true, selector) => topicMatch(selector, topic));
|
|
@@ -257,24 +256,46 @@ class ClickHouse {
|
|
|
257
256
|
row.Payload = JSON.stringify(value);
|
|
258
257
|
} // else: omit
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
table: this.mqttHistoryTable,
|
|
263
|
-
values: [row],
|
|
264
|
-
format: 'JSONEachRow'
|
|
265
|
-
});
|
|
266
|
-
} catch (error) {
|
|
267
|
-
console.error('Error inserting MQTT message into ClickHouse:', error.message);
|
|
268
|
-
}
|
|
259
|
+
// cache it for batch insertion
|
|
260
|
+
this.addToCache(this.mqttHistoryTable, [row]);
|
|
269
261
|
})
|
|
270
262
|
});
|
|
263
|
+
}
|
|
271
264
|
|
|
265
|
+
this.mqttHistoryTable = tableName;
|
|
272
266
|
|
|
267
|
+
// start interval for batch insertion
|
|
268
|
+
if (!this.insertionInterval) {
|
|
269
|
+
this.insertionInterval =
|
|
270
|
+
setInterval(this.batchInsertCache.bind(this), interval);
|
|
273
271
|
}
|
|
272
|
+
}
|
|
274
273
|
|
|
275
|
-
|
|
274
|
+
/** Add the given rows to the cache for batch-insertion to the given table */
|
|
275
|
+
addToCache(table, rows) {
|
|
276
|
+
this.rowCache[this.mqttHistoryTable] ||= [];
|
|
277
|
+
this.rowCache[this.mqttHistoryTable].push(...rows);
|
|
276
278
|
}
|
|
277
279
|
|
|
280
|
+
/** Function responsible fgor inserting all cached rows */
|
|
281
|
+
batchInsertCache() {
|
|
282
|
+
_.forEach(this.rowCache, (rows, table) => {
|
|
283
|
+
if (rows.length == 0) return;
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
this.rowCache[table] = [];
|
|
287
|
+
this.client.insert({
|
|
288
|
+
table,
|
|
289
|
+
values: rows,
|
|
290
|
+
format: 'JSONEachRow'
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error(`Error inserting ${rows.length} rows into ${table}`, error.message);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
|
|
278
299
|
/* Register an MQTT topic for storage in ClickHouse subscribes to the topic
|
|
279
300
|
* and stores incoming messages JSON.stringify'd in a ClickHouse table.
|
|
280
301
|
* Retrieve using `queryMQTTHistory`, or, when quering directly, e.g., from
|
package/package.json
CHANGED
package/test/clickhouse.test.js
CHANGED
|
@@ -63,7 +63,7 @@ describe('ClickHouse', function() {
|
|
|
63
63
|
await clickhouse.enableHistory({
|
|
64
64
|
dataCache,
|
|
65
65
|
tableName: TABLE_NAME
|
|
66
|
-
});
|
|
66
|
+
}, 100);
|
|
67
67
|
|
|
68
68
|
await clickhouse.registerMqttTopicForStorage(STANDARD_TOPIC_PATTERN);
|
|
69
69
|
});
|
|
@@ -130,7 +130,6 @@ describe('ClickHouse', function() {
|
|
|
130
130
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
131
131
|
dataCache.update([org, 'device1', 'data'], null);
|
|
132
132
|
await once(emitter, 'insert');
|
|
133
|
-
await once(emitter, 'insert');
|
|
134
133
|
|
|
135
134
|
const rows = await queryRowsByOrg(org);
|
|
136
135
|
|