Validate what a ClickHouse query actually costs before merging it — at production scale through read-only environment access, or on a revived Testcontainers dataset extrapolated to 20k/500k/1M entities. Use when a DAO query changes, when an endpoint is slow, or when a reviewer asks "what does this cost at scale".
72
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Turn "this query looks expensive" into numbers a reviewer can act on. The output is a verdict per clause — change, keep, or caller's call — each attached to a measurement, plus the negative results so nobody retries them.
ORDER BY, and anything paginated, where order decides which rows land on the page —
the comparison must include that order; compare order-independently only when the result genuinely
has no defined order. A faster query that answers a different question is not an optimization.
(Equivalence holds between the candidate and its variants. A candidate is often meant to change
results versus main; main is the cost reference, not a result reference.)The failure mode of this work is a confident mechanism the engine does not implement.
IN (SELECT …) are often built eagerly and
appear only as a literal (x in 35297-element set) with no read node — so counting
ReadFromMergeTree nodes undercounts work, and a dominant subquery can be invisible.EXPLAIN executes scalar and set subqueries to resolve index conditions: it is neither free nor
a timing proxy.main — see rendering.md.environments.md.main, ≥5 runs each, plan captured per call site, shape
recorded alongside. The delta between them is the cost of the change, and it is a finding in its
own right — often the one that matters most.instrumentation.md. Isolate the suspect subquery and measure
end-to-end; a gap between them is itself a finding.Put the four measured dimensions side by side — latency (p50, p90, p95, min), peak memory, CPU time, scanned/read (parts, granules, marks, rows) — for every shape and every call site, and decide from the whole set:
Make the scan numbers do their job: they should explain the latency, CPU and memory you measured. If they do not — scan is flat but memory doubled, or rows fell but CPU rose — you have not found the mechanism yet, and the verdict is not ready.
Whether you are reviewing a PR or opening one, justify every claim with a before/after table — one row per variant (or per revision), one column per measured dimension:
| variant | p50 ms | p90 ms | p95 ms | CPU ms | peak MiB | parts | granules (marks) | rows read |
|---|---|---|---|---|---|---|---|---|
main | … | … | … | … | … | … | … | … |
| candidate (before) | … | … | … | … | … | … | … | … |
| with the change (after) | … | … | … | … | … | … | … | … |
The scan columns are not optional: they are what makes the other three explainable, so carry the pruning evidence into the table rather than only the totals.
One table per call site and per shape, with the shape named (entity counts, run count). A verdict without its table is an opinion, and prose alone hides exactly the trade a reviewer needs to see.
Lead with what the change itself costs against main, per call site — a tuning delta of a few percent
does not outrank the endpoint getting materially more expensive, and if that cost is not acceptable,
say so and name the lever that would actually move it.
Then per clause: change (the replacement, why, before → after table), keep (what you tried and the number that killed it), or caller's call (both options, trade named). Then the equivalence evidence and the caveats. Do not include optimizations you did not measure, or a mechanism you did not verify.
opik-backend (clickhouse.md, testing.md) — DAO and ClickHouse conventions, plus the
Testcontainers rules including "one ClickHouse-migrating test class per mvn invocation".
657e319
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.