Skip to content

fix(clickhouse): enforce time and read limits on every query - #77

Merged
samcm merged 2 commits into
masterfrom
fix/query-read-limits
Nov 6, 2026
Merged

samcm merged 2 commits into
masterfrom
fix/query-read-limits

Conversation

@samcm

@samcm samcm commented Nov 6, 2026

Copy link
Copy Markdown
Member

Lab data-quality audit (API-2), approved by the Lab coordinator.

Problem. max_execution_time was a connection setting. Over HTTP the driver sends it as a URL parameter, and chproxy (between cbt-api and ClickHouse in production) strips URL parameters. So no query ever had a limit, and the readonly server default is 0. On 2026-11-05, scans ran up to 19 TiB and 745 s, long after cbt-api had returned its 30 s error.

Fix.

  • Every query gets a SETTINGS clause in its text, which survives any proxy: max_execution_time (default now 20 s, was 60), max_rows_to_read (default 1,000,000,000) and max_bytes_to_read (default 64 GiB), with read_overflow_mode = 'throw'. All three are configurable; 0 disables one.
  • A query that hits a limit (ClickHouse codes 158, 159, 307) returns 400: "query reads too much data or runs too long: filter the table's primary_key field to a smaller range inside its bounds (see /api/v1/{network}/tables/{table})". Other query errors stay 500.
  • The handlers also check rows.Err(), so a limit that fires after rows started streaming returns the 400, not a silently short page.
  • Open-ended ranges are not rejected; the time limit handles them.
  • cancel_http_readonly_queries_on_client_close is not set: it is read from the HTTP request, which chproxy filters, and the server-side max_execution_time now ends the query anyway.

Sizing (lab-backend's queries over 7 days, from query_log): p99 901K rows / 109 MiB, max 717M rows / 47.7 GiB (state-expiry address_like=0x%, custody 19-day probe dialog). The caps sit above the measured max. In production today the heaviest Lab requests take 0.4–4.2 s, well under 20 s.

Tests.

  • New TestClient_ReadLimits against a real ClickHouse over native and HTTP: a query under max_rows_to_read succeeds, one over it fails and maps to 400.
  • TestClient_QueryTimeout failed on master (latest master run): its 1B-number sum could finish inside the 100 ms deadline and then hit a scan error. It now uses sleep(2).
  • make test passes locally: 474 unit tests and the integration suite.

Deploy note: platform's xatu-cbt-api values set max_execution_time: 60 explicitly; change it to 20 (or drop it) in the bump.

max_execution_time was set as a connection setting, which the HTTP
driver sends as a URL parameter, and chproxy strips URL parameters. So
no query had a limit: 11-05 saw scans of up to 19 TiB and 745 s.

Every query now carries a SETTINGS clause in its text, which reaches
ClickHouse through any proxy: max_execution_time (default 20 s),
max_rows_to_read (1B) and max_bytes_to_read (64 GiB) with
read_overflow_mode 'throw'. A query that hits one of them returns a 400
that tells the caller to narrow its primary-key range, not a 500, also
when the limit fires after the first rows have streamed.

Also makes TestClient_QueryTimeout outlast its deadline: summing 1B
numbers could finish inside 100 ms, and it failed on master.

Co-authored-by: Sam Calder-Mason <samcaldermason@gmail.com>
@samcm
samcm requested a review from mattevans as a code owner November 6, 2026 02:47
Amp-Thread-ID: https://ampcode.com/threads/T-01a1a60e-fffe-740c-b50a-32436a4f6230
Co-authored-by: Sam Calder-Mason <samcaldermason@gmail.com>
@samcm
samcm merged commit 87f8f77 into master Nov 6, 2026
3 checks passed
@samcm
samcm deleted the fix/query-read-limits branch November 6, 2026 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants