Files
datastore/ci/jobs/scripts/fuzzer/limit-recursion-settings.xml
Groene AIandClaude Opus 4.8 bfe3c784ed Cap test-only sleep settings via fuzzer profile constraints
Replace the QueryFuzzer name-based guard (and its gtest) with a max
constraint on sleep_in_send_data_ms, sleep_in_send_tables_status_ms and
sleep_after_receiving_query_ms in limit-recursion-settings.xml, alongside
the existing max_threads cap and for the same reason: the fuzzer scales a
test's small value into a multi-minute TCPHandler sleep_for (uninterruptible
by cancellation) that trips the 90s hung-check. The profile is installed by
both the AST fuzzer and the stress test, so the cap applies wherever the
fuzzer runs. Legit stateless uses are <= 100ms; a fuzzed value is clamped
to 1s and cannot trip the hung-check.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-12 03:11:28 +00:00

81 lines
3.9 KiB
XML

<clickhouse>
<profiles>
<default>
<constraints>
<!--
Prevent pathologically slow/hung queries built from deeply nested
expressions (e.g. `SELECT formatQuery($$SELECT [[[[...20000...]]]]$$)`).
Bound the parser/AST depth and size settings to [1, default] so neither
the query fuzzer nor a test can raise them to values that build huge,
slow-to-parse/format queries. The `<min>1</min>` matters: for all of these
a value of `0` means "unlimited" and disables the guard, so a `<max>` alone
would still let `SET ... = 0` remove the safeguard. Lowering to any value
>= 1 is still allowed.
Installed by both the AST fuzzer and the stress test, since the stress
test ignores `no-*` build tags and runs the fuzzer over every query.
`max_parser_backtracks` is additionally pinned on the client command line
(run-fuzzer.sh, stress.py): its pre-24.3 default is 0, so a client
`compatibility=...` reverts it to 0 and sends that to the server, which a
value in this profile cannot prevent (the client value wins).
-->
<max_ast_depth>
<min>1</min>
<max>1000</max>
</max_ast_depth>
<max_parser_depth>
<min>1</min>
<max>1000</max>
</max_parser_depth>
<max_parser_backtracks>
<min>1</min>
<max>1000000</max>
</max_parser_backtracks>
<max_ast_elements>
<min>1</min>
<max>50000</max>
</max_ast_elements>
<max_expanded_ast_elements>
<min>1</min>
<max>500000</max>
</max_expanded_ast_elements>
<!--
A fuzzed huge max_threads (e.g. 65535) makes one query request more
pipeline threads than the global pool (max_thread_pool_size=10000),
saturating it (CANNOT_SCHEDULE_TASK), which starves the server-liveness
probe and delays query cancellation. Cap it. Lives here so every
ast_fuzzer_runs user (dedicated AST fuzzer and stress test) gets it.
-->
<max_threads>
<max>128</max>
</max_threads>
<!--
Test-only fault-injection delays. TCPHandler runs them via
std::this_thread::sleep_for, which does not observe query cancellation, so a
fuzzed multi-minute value keeps the connection thread blocked past the 90s
hung-check window on a cancelled query. Cap them: legit stateless uses are
<= 100ms, and clamping a fuzzed value to 1s cannot trip the hung check.
-->
<sleep_in_send_data_ms>
<max>1000</max>
</sleep_in_send_data_ms>
<sleep_in_send_tables_status_ms>
<max>1000</max>
</sleep_in_send_tables_status_ms>
<sleep_after_receiving_query_ms>
<max>1000</max>
</sleep_after_receiving_query_ms>
</constraints>
<!-- Server-side backstop: keeps max_parser_backtracks `changed` so a server-side
`SET compatibility=...` can't revert it to 0. The client-option compatibility
path is handled by the command-line pin in run-fuzzer.sh / stress.py, which
overrides this value anyway. -->
<max_parser_backtracks>1000000</max_parser_backtracks>
</default>
</profiles>
</clickhouse>