@voltro/plugin-clickhouse 0.33.0 → 0.35.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/dist/index.d.ts CHANGED
@@ -47,16 +47,28 @@ export declare interface ClickhouseAnalyticsOptions {
47
47
  readonly mirrorTables?: ReadonlyArray<string>;
48
48
  /** Primary-key column on the mirrored source rows. Default `'id'`. */
49
49
  readonly mirrorPrimaryKey?: string;
50
- /** Opt into client-side batching of `track()` inserts. Omit for immediate
51
- * per-event inserts (a successful `track` = the row was accepted). See the
52
- * batching note in the README for the delivery-semantics trade-off. */
53
- readonly batch?: ClickhouseBatchOptions;
50
+ /**
51
+ * Client-side batching of `track()` inserts ON by default (20 events /
52
+ * 5 s, plugin-posthog's numbers). What changes observably with batching: a
53
+ * successful `track()` means "buffered", the actual insert happens off the
54
+ * call path (flushed by size, on the timer, and on graceful shutdown via
55
+ * `dispose`; a flush failure is logged + that batch dropped; a hard crash
56
+ * loses whatever is still buffered).
57
+ *
58
+ * `batch: false` opts OUT: every `track()` is one immediate HTTP insert and
59
+ * a success means ClickHouse accepted the row — at one round-trip and one
60
+ * MergeTree part per event, which is the part-explosion PERF-11 measured.
61
+ * `batch: {...}` keeps batching and tunes size/interval.
62
+ */
63
+ readonly batch?: ClickhouseBatchOptions | false;
54
64
  }
55
65
 
56
- /** Opt-in client-side batching. Buffers `track()` rows and flushes them in
57
- * ONE multi-row `client.insert`, cutting HTTP round-trips under load. */
66
+ /** Client-side batching tunables (batching is ON by default). Buffers
67
+ * `track()` rows and flushes them in ONE multi-row `client.insert`, cutting
68
+ * HTTP round-trips and MergeTree part churn under load. */
58
69
  export declare interface ClickhouseBatchOptions {
59
- /** Flush once the buffer reaches this many events. Default 1000. */
70
+ /** Flush once the buffer reaches this many events. Default 20 (mirrors
71
+ * plugin-posthog's conservative batcher). */
60
72
  readonly maxSize?: number;
61
73
  /** Flush a non-empty buffer this many ms after the first event lands
62
74
  * (so low-volume trickles don't sit until shutdown). Default 5000. */
@@ -70,8 +82,13 @@ export declare const EVENTS_TABLE_DDL: (database: string, table: string) => stri
70
82
  * gives idempotent upserts — re-inserting the same row with a newer
71
83
  * `version` collapses to the latest on merge. Deletes insert a tombstone
72
84
  * (`is_deleted = 1`); queries filter `is_deleted = 0` (or use the
73
- * `FINAL` modifier). `version` is a monotonic micro-timestamp so a later
74
- * write always wins.
85
+ * `FINAL` modifier).
86
+ *
87
+ * `version` is the framework's per-change ordering token
88
+ * (`MirrorVersion`), assigned in COMMIT order and carried on the write.
89
+ * It is never read from a clock inside this sink — that is what makes
90
+ * "a later write always wins" true of the later COMMIT rather than of
91
+ * whichever HTTP request happened to arrive last.
75
92
  */
76
93
  export declare const MIRROR_TABLE_DDL: (database: string, sourceTable: string) => string;
77
94
 
package/dist/index.js CHANGED
@@ -138,23 +138,37 @@ var a = r({ scope: "@voltro/plugin-clickhouse" }), o = "clickhouse", s = (e) =>
138
138
  return {
139
139
  tables: e.mirrorTables,
140
140
  primaryKey: e.mirrorPrimaryKey,
141
- upsert: (r, i) => t.gen(function* () {
141
+ upsert: ({ table: r, row: i, version: a }) => t.gen(function* () {
142
142
  let t = String(i[e.mirrorPrimaryKey]);
143
143
  yield* n(r, {
144
144
  id: t,
145
145
  data: JSON.stringify(i),
146
- version: Date.now() * 1e3,
146
+ version: a,
147
147
  is_deleted: 0
148
148
  });
149
149
  }).pipe(d("mirror.upsert")),
150
- remove: (e, r) => t.gen(function* () {
150
+ remove: ({ table: e, primaryKeyValue: r, version: i }) => t.gen(function* () {
151
151
  yield* n(e, {
152
152
  id: String(r),
153
153
  data: "{}",
154
- version: Date.now() * 1e3,
154
+ version: i,
155
155
  is_deleted: 1
156
156
  });
157
- }).pipe(d("mirror.remove"))
157
+ }).pipe(d("mirror.remove")),
158
+ maxVersion: ({ table: n, primaryKeyValue: r }) => t.gen(function* () {
159
+ p(String(n), "mirror table");
160
+ let i = yield* _(e), a = `SELECT max(version) AS v, count() AS n FROM ${e.database}.${s(String(n))} WHERE id = {id: String} FORMAT JSON`, o = yield* t.tryPromise({
161
+ try: () => i.query({
162
+ query: a,
163
+ query_params: { id: String(r) }
164
+ }),
165
+ catch: (e) => e
166
+ }), { data: c } = yield* t.tryPromise({
167
+ try: () => o.json(),
168
+ catch: (e) => e
169
+ }), l = c[0];
170
+ return l === void 0 || Number(l.n) === 0 || l.v === null ? null : Number(l.v);
171
+ }).pipe(d("mirror.maxVersion"))
158
172
  };
159
173
  }, O = (e) => {
160
174
  let n = e.mirrorTables.length > 0 ? D(e) : void 0;
@@ -248,7 +262,7 @@ var a = r({ scope: "@voltro/plugin-clickhouse" }), o = "clickhouse", s = (e) =>
248
262
  }).pipe(d("topN")),
249
263
  ...n === void 0 ? {} : { mirror: n }
250
264
  };
251
- }, k = 1e3, A = 5e3, j = (n) => {
265
+ }, k = 20, A = 5e3, j = (n) => {
252
266
  let r = (n.mirrorTables ?? []).map((e) => p(e, "mirror table")), i = {
253
267
  client: null,
254
268
  database: p(n.database ?? "default", "database"),
@@ -256,10 +270,14 @@ var a = r({ scope: "@voltro/plugin-clickhouse" }), o = "clickhouse", s = (e) =>
256
270
  mirrorTables: r,
257
271
  mirrorPrimaryKey: n.mirrorPrimaryKey ?? "id"
258
272
  };
259
- return n.batch && (i.batcher = b(i, {
260
- maxSize: n.batch.maxSize ?? k,
261
- flushIntervalMs: n.batch.flushIntervalMs ?? A
262
- })), {
273
+ if (n.batch !== !1) {
274
+ let e = n.batch ?? {};
275
+ i.batcher = b(i, {
276
+ maxSize: e.maxSize ?? k,
277
+ flushIntervalMs: e.flushIntervalMs ?? A
278
+ });
279
+ }
280
+ return {
263
281
  kind: "clickhouse",
264
282
  build: (e) => O(i),
265
283
  initialize: () => t.gen(function* () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-clickhouse",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
4
4
  "description": "ClickHouse AnalyticsSink. Native HTTP ingest into a ClickHouse cluster — self-hosted OR ClickHouse Cloud. Production-grade OLAP for high-cardinality events with millisecond aggregate queries over billions of rows.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -34,8 +34,8 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@clickhouse/client": "^1.23.1",
37
- "@voltro/logger": "0.33.0",
38
- "@voltro/runtime": "0.33.0"
37
+ "@voltro/logger": "0.35.0",
38
+ "@voltro/runtime": "0.35.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "effect": "^3.22.0"