fix(clickhouse): add execution time cap to task run metrics queries#4381
fix(clickhouse): add execution time cap to task run metrics queries#4381claude[bot] wants to merge 1 commit into
Conversation
`getTaskActivity` and `getAverageDurations` were registered without any `clickhouseSettings`, so neither had a server-side `max_execution_time`. Their sibling `existsQueryBuilder` in the same object literal already passes one. With no server cap, the only thing bounding a slow query is the client's `request_timeout`. That aborts the HTTP request rather than the query, so the failure surfaces as an untyped socket timeout instead of a ClickHouse timeout error, and stopping the query itself depends on the server noticing the disconnect. Set `max_execution_time: 25` on both. `@clickhouse/client` defaults `request_timeout` to 30000ms and nothing in this repo overrides it, so 25s sits below the client timeout — ClickHouse terminates the query and returns a typed error before the client gives up. Mirroring the neighbouring `10` would instead start failing queries that currently succeed in the 10-30s range. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PtQ34usZSzrSALPVBmoLYA
|
|
Closing this in favour of #4380. That PR removes the last callers of The underlying concern — read paths that can only be stopped by the client's The branch Generated by Claude Code |
Requested via Slack thread
Two of the task run metrics queries are registered with no
clickhouseSettings, so they have no server-sidemax_execution_time— nothing tells ClickHouse to give up on them. Their sibling in the same object literal already has one.Before / After
Before
getTaskActivityandgetAverageDurationswere registered asgetTaskActivityQueryBuilder(this.reader)andgetAverageDurations(this.reader)— no settings argument, so no execution cap reached the server.request_timeout. That is a client-side abort of the HTTP request, not a cancellation of the query: it tears down the socket after 30s and the caller gets an untyped socket timeout, with no indication of which query ran long or why.After
max_execution_time: 25.How
One-line change on each of the two registrations in
internal-packages/clickhouse/src/index.ts:Both builders already accepted an optional
settings?: ClickHouseSettingssecond argument and forwarded it toch.query({ ..., settings }), so no signature or plumbing changes were needed.Why 25 and not 10
The neighbouring
existsQueryBuilderuses{ max_execution_time: 10 }, but copying that value here would be a behaviour regression rather than a safeguard: any of these metrics queries that currently completes in the 10–30s window would start failing where today it succeeds. A cap that newly breaks working queries is worse than the problem it fixes.25s was picked to sit just under the client timeout, verified rather than assumed:
internal-packages/clickhouse/package.jsondepends on@clickhouse/client: ^1.12.1, resolved to1.12.1inpnpm-lock.yaml.request_timeout: config.request_timeout ?? 30000(@clickhouse/client-common/dist/config.js), i.e. 30s.request_timeoutis not set anywhere in this repo —createClientinsrc/client/client.tspasseskeep_alive,http_agent,compression,max_open_connections,clickhouse_settingsandlog, and nothing else — so the 30s default is what's in effect.So 25s is comfortably above normal latency for these queries, and comfortably below the 30s point at which the client would abort. The server wins the race, which is the whole point: ClickHouse enforces the limit and returns a typed error naming the query, instead of the client blindly tearing down a request.
For reference, the repo already treats this as the pattern elsewhere —
queryService.server.tssetsmax_execution_timefromQUERY_CLICKHOUSE_MAX_EXECUTION_TIME, and the logs client sets it fromCLICKHOUSE_LOGS_LIST_MAX_EXECUTION_TIME. These two registrations were simply missed.One thing worth flagging for review: the client does set
cancel_http_readonly_queries_on_client_close: 1, so on a clean socket teardown ClickHouse will usually cancel the query anyway. That mitigates the wasted-work half of the problem but not the error-quality half, and it makes termination contingent on connection handling rather than on an explicit server-side limit. An explicit cap is the more reliable of the two.Deliberately out of scope: the
statusskip-index /FINALquestion ontask_runs_v2. That is schema work and needs a schema owner; this PR is only the missing per-query cap.✅ Checklist
Testing
pnpm run typecheck --filter @internal/clickhouse— passes.pnpm run typecheck --filter webapp— passes (the webapp is the consumer of these two registrations).pnpm run formatandpnpm run lint:fix— clean, no changes produced.@clickhouse/client-commonrather than assumed, and the repo was checked for anyrequest_timeoutoverride (there is none).Changelog
Server-only change, so this carries a
.server-changes/note rather than a changeset:.server-changes/task-run-metrics-query-time-limit.md.Screenshots
No visual change.
Generated by Claude Code