cvx-ch-sync@ch-sync/componentsource

Convex ClickHouse · reactive sync

ClickHouse,
but reactive.

Every Convex write streams into ClickHouse through an outbox. When the insert is acked, a scoped watermark bumps and every query watching it refetches. No polling.

commit fresh aggregate

000ms

press mutate and follow the row

events.add(…)
mutation—

events.add

outbox—

component table

empty
debounce 250ms

drain.run—

action

idle

—
batchSize 200

ClickHouse—

JSONEachRow insert

0 rows
async_insert=1

watermarks—

bump after ack

events—

/team:t1—

/team:t2—

client—

useClickHouseQuery

subscribed · team:t1

0 refetches
push —

chsync.eventsFINAL · ReplacingMergeTree(ver, is_deleted)0 rows

docIdString

verUInt64

teamIdString

userIdString

kindString

valueFloat64

createdAtDateTime64(3)

eventsByKindwm —
SELECT kind, count() AS n, sum(value) AS total
FROM events FINAL WHERE teamId = {teamId:String} GROUP BY kind
0clickΣ 0
0viewΣ 0
0purchaseΣ 0

Team t1

case notesJun 2026

cvx-ch-sync

Replicates Convex tables into ClickHouse in ~300ms, and keeps queries reactive

What it is

A Convex component that streams table writes into ClickHouse through an outbox, then bumps a watermark so analytics queries refetch the moment fresh rows land. Your dashboards stay live without polling.

The bit I'm proud of

It keeps ClickHouse queries reactive, the way Convex queries already are. Writes land in about 200 to 500ms.

On this tab: The pipeline itself. Press the button and follow the row.