AI agents don't query like humans
AI agents query ClickHouse® with many small lookups by key, in parallel bursts, with retries. In our tests on a 200-billion-row dataset, an agent query read about 250 times fewer rows than an analyst's and took about as long. Sixteen agents in parallel got fewer than twice the answers of one. Size the database for agent concurrency, cache and isolation before scan speed.
Most ClickHouse clusters were tuned for a person with a SQL editor: a few wide scans an hour, a chart, a coffee, the next scan. That person is losing the main-caller seat to agents, and agents hit the same data in a shape the cluster wasn't sized for.
Rafa already wrote about what that volume does to a data warehouse bill and what to do with the data agents write. This post is about what it does to the database, and what we changed because of it.
How AI agents query a database
An AI agent's session against a database is a loop: read the schema, probe one key, drill into whatever came back, fix the query that failed, and ask again.
An analyst opens a SQL editor already knowing the tables, writes one wide query, reads the result for a minute, and writes the next one. An agent starts from zero on every session. Its first call reads system.columns or runs DESCRIBE, the second is a narrow probe with a LIMIT, and from there it follows the entity it found into the next table.
A wrong column name comes back as an error, so the agent reads the schema again and retries. An answer too long for its context window gets asked again, smaller. Our own scheduled agents leave run logs that read like a transcript of that loop.
Small reads, many of them, from several agents at once and at any hour.
What changes when agents are the main caller
Rows stop predicting latency, concurrency becomes the ceiling, and shared compute stops working. We saw all three when we pointed agents and analysts at the same 200-billion-row dataset.
The median agent query read 0.54 million rows. The median analyst query read 134 million. They came back in about the same time. A lookup by key still has to open every data part that might hold the key, and on a table that size that came to hundreds of parts for a 20-row answer.
Then we added agents. Going from one agent session to sixteen in parallel sent three times as many queries and got fewer than twice as many answers back. The whole fleet read under 1 GB a minute while it stalled, so the limit sat in how many lookups the cluster could hold in flight, far below its bandwidth.
The last result is the one I'd show anyone planning to give agents access to the cluster that runs their reports. With two analyst reports running on the same nodes, eight agent sessions got zero lookups back in a minute. Only their schema reads came through.
Throughput: what moves the limit for agent traffic
Two levers move it: fewer round trips per lookup, and fewer heavy neighbours in the queue.
Cache is the first. A lookup repeated seconds later came back 12 times faster, because the data was already in our distributed cache. Agents re-read what they touched a step earlier: the entity from the last call, the schema from the first, the table they keep drilling into. A cache sized to that working set turns most of an agent's round trips into local reads, so we size our distributed cache per workload, as covered in our scaling setup. With the cache sized and the cluster tuned, a thousand random address lookups over 203 billion rows held a 703 ms p99, none over a second.
Isolation is the second. We run agent work on its own compute over the same storage, so a report scan and an agent lookup don't share a queue. Datashares go a step further for agents that read another team's data: the agent's service reads the shared tables read-only, on its own compute, with no copy.
Cost: what agent traffic does to a ClickHouse bill
Per-query pricing punishes agents. Every call is a line item, and one agent question took seven queries in our tests, so the bill grows with every agent you add.
Per-byte pricing makes an agent query look free, because it reads half a million rows. It also misses where the money goes. Agents use up concurrent capacity: lookups in flight, and memory to keep their working set warm. You need compute sized for the sessions you run at peak, a cache large enough for what those sessions keep touching, and separate compute so agents and reports don't starve each other.
Our pricing follows that shape. You pay for storage, and you or your agent provision the compute the workload needs. The cache runs on the NVMe drives of nodes you already pay for, and there is no charge per query or per byte scanned. The full bill is in our cost setup. The short version for agents: estimate peak sessions, size compute and cache for them, and resize when the count grows.
Schema and indexes when AI agents are the main caller
The sort key decides which caller is cheap.
Our test table was sorted by account, then time. Agent lookups by account used the primary index and stayed small. An analyst question about yesterday read half a billion rows and gave up, because a time filter on an account-first table can only skip whole parts.
Agents look things up by entity: a user, an account, a session, an order. On the tables agents hit, put that entity first in ORDER BY, and give the time-shaped questions their own path through a rollup or a projection ordered by time. We covered when to pick which in index vs projection vs materialized view.
Keep the part count down on lookup tables. Monthly partitions are right for event tables that need retention and backfills, but every partition is another place a key lookup has to check. Fewer partitions and fewer, larger parts let a lookup open tens of parts where ours opened hundreds.
Don't let rollups multiply. One team we spoke with told their engineers' agents to query rollup tables because the event table was too slow. Within a month they had dozens of rollups, several refreshing every few minutes, and the database caught fire about once a week. Make the base table fast enough for agent lookups and keep rollups for the questions people ask every week.
Treat the schema as documentation, because it's the only documentation an agent reads. Column COMMENTs and plain names are what it gets back from DESCRIBE. A column called amount_usd that holds cents will produce a confident wrong answer.
To see how your own agent traffic behaves, group the query log by caller:
SELECT
coalesce(nullIf(log_comment, ''), nullIf(http_user_agent, ''), client_name) AS caller,
count() AS queries,
round(quantile(0.95)(query_duration_ms)) AS p95_ms,
quantile(0.5)(read_rows) AS median_rows,
round(avg(ProfileEvents['SelectedParts'])) AS avg_parts,
round(100 * avg(exception_code != 0), 1) AS error_pct
FROM system.query_log
WHERE event_time > now() - INTERVAL 1 DAY
AND type IN ('QueryFinish', 'ExceptionWhileProcessing')
AND query_kind = 'Select'
GROUP BY caller
ORDER BY queries DESC
LIMIT 20;
High avg_parts with low median_rows is the agent signature. Start with the callers that show it.
Guardrails for agent callers
500 rows, 60 seconds, four queries at a time. Those are the limits on the SQL tool our own agents use against our company data in ClickHouse:
| Setting | Value | Why |
|---|---|---|
readonly | 1 | Blocks writes, and blocks SETTINGS clauses that would lift the other limits from inside the SQL |
max_execution_time | 60 s | A lost agent can't hold a slot for an hour |
| Result cap | 500 rows, truncation flagged | Keeps answers inside a context window and tells the agent to narrow the query |
| Concurrent queries per request | 4 | Caps how far one request can fan out |
| Errors | returned as data, with a hint | An unknown column comes back with a hint to check the names, so the retry is right the second time |
log_comment | the caller on every query | Makes the query log readable by caller |
These limits decide how much one confused session can cost. We made the case for one SQL tool with good context over dozens of narrow tools in One powerful tool, not forty.
Which limit you are hitting, and what moves it
Agent latency that climbs with sessions while bytes stay small points at parts and cache. Agents stalling while reports run points at shared compute.
| Symptom | Check | Move |
|---|---|---|
| Agent p95 grows with sessions while CPU sits idle | Parts per query, cache hit rate | Cache sized to the working set, fewer and larger parts on lookup tables |
| Agent lookups are slow, reports are fine | Sort key against the agent's filter | Entity-first ORDER BY, or a projection ordered by the entity |
| Agents stall while reports run | Shared compute | Separate compute for agent work over the same storage |
| The number of rollup tables keeps growing | Who created each one, and for which question | Speed up the base path, keep rollups for repeated human questions |
| You can't tell agent traffic from human traffic | log_comment, http_user_agent, users | Tag every agent query at the tool |
Agents are becoming the main caller of your database. Size it for concurrency, cache and isolation before you size it for scan speed. ObsessionDB runs that setup: storage priced on its own, compute you or your agents provision per workload, a distributed cache in front of object storage, and separate compute groups over one copy of the data. If your query log already shows agent callers, our cost audit reads it by caller and tells you which of these limits you are hitting.
FAQ
AI agents send many small queries by key instead of a few large scans. In our test on a 200-billion-row dataset, the median agent query read 0.54 million rows against 134 million for an analyst's. Agents also read the schema first, retry after errors, and run several sessions in parallel.
Yes, if you size it for the agent pattern. ClickHouse answers key lookups through its primary index, so entity-first sort keys serve agents well. The limits come from concurrency: parts opened per lookup, cache hit rate, and agents sharing compute with heavy reports. Our test hit those limits while the agent fleet read under 1 GB a minute.
ClickHouse caps concurrent queries server-wide with `max_concurrent_queries`, but agent traffic hits a lower ceiling first. In our tests, sixteen parallel agent sessions got fewer than twice the answers of one while reading under 1 GB a minute. Parts opened per lookup and round trips to object storage set the real limit.
Read-only access with limits works well. Our agents connect with `readonly=1`, a 60-second timeout, capped result sizes, at most four queries at a time per request and a `log_comment` tag on every query. Run agent sessions on separate compute so a confused agent can't slow customer-facing queries.
Tag them at the tool. Set `log_comment` on every query an agent sends, or give agents their own ClickHouse user, then group `system.query_log` by that tag. Without a tag, `http_user_agent` and `client_name` sometimes identify the client library, but they rarely separate an agent from the engineer running it.
Agents add questions and concurrency, and their queries read little data. On per-query pricing that raises the bill with every question. On provisioned compute the cost follows the capacity you size for peak sessions and cache, and it changes when you resize that capacity.
Continue Reading
Originally written for obsessionDB. Read the original article here.
ClickHouse is a registered trademark of ClickHouse, Inc. https://clickhouse.com