Motivation
Async consumers (energydb in particular) reach ClickHouse through asyncio.to_thread(sync_call) today, at three sites in energydb's _io.py. Profiling on the engine read path shows ch_ms is 94 to 98% of read wall time (45.1 of 47.5 ms at one series, 3,785 of 3,795 ms at 6,000 series), so almost everything that currently pins a worker thread is pure network wait.
clickhouse-connect (already pinned >=1.7.2) ships a real aiohttp-based AsyncClient. Its query_arrow awaits the network natively and offloads only the pyarrow IPC parse to an executor, which is exactly the split timedb wants.
A second benefit: the network phase becomes cancellable. A to_thread CH call is not, as the comment in energydb's _io.py puts it: "await it or the query thread outlives the read that started it."
Proposal
Add a native async client path to timedb (e.g. an AsyncTimeDBClient alongside the existing sync TimeDBClient) built on clickhouse_connect.get_async_client, and move async consumers' read/write paths onto it.
Scope notes
TimeDBClient is synchronous top to bottom and the published wheel has sync consumers, so this is an API addition, not a drop-in swap.
- One clickhouse-connect
AsyncClient binds to one event loop; lifecycle needs to account for that.
- The
clickhouse-connect[async] extra must be declared. No version pin bump is needed.
🤖 Generated with Claude Code
Motivation
Async consumers (energydb in particular) reach ClickHouse through
asyncio.to_thread(sync_call)today, at three sites in energydb's_io.py. Profiling on the engine read path showsch_msis 94 to 98% of read wall time (45.1 of 47.5 ms at one series, 3,785 of 3,795 ms at 6,000 series), so almost everything that currently pins a worker thread is pure network wait.clickhouse-connect (already pinned
>=1.7.2) ships a real aiohttp-basedAsyncClient. Itsquery_arrowawaits the network natively and offloads only the pyarrow IPC parse to an executor, which is exactly the split timedb wants.A second benefit: the network phase becomes cancellable. A
to_threadCH call is not, as the comment in energydb's_io.pyputs it: "await it or the query thread outlives the read that started it."Proposal
Add a native async client path to timedb (e.g. an
AsyncTimeDBClientalongside the existing syncTimeDBClient) built onclickhouse_connect.get_async_client, and move async consumers' read/write paths onto it.Scope notes
TimeDBClientis synchronous top to bottom and the published wheel has sync consumers, so this is an API addition, not a drop-in swap.AsyncClientbinds to one event loop; lifecycle needs to account for that.clickhouse-connect[async]extra must be declared. No version pin bump is needed.🤖 Generated with Claude Code