Fix fuzzer status.tsv write aborting on "server_died: unbound variable"#107900
Conversation
In run-fuzzer.sh the `fuzz` function writes the run outcome to status.tsv with `echo -e "$server_died\t$server_exit_code\t$fuzzer_exit_code"`. Of the three variables, `server_died` is the only one never initialized before use: it is assigned solely inside the post-fuzz server-liveness loop, at server_died=0 (SELECT 1 succeeds, then break) or server_died=1 (server is unreachable, then break). Two branches of that loop, TOO_MANY_SIMULTANEOUS_QUERIES and MEMORY_LIMIT_EXCEEDED (added in edecdd5), sleep and continue without assigning server_died or breaking. When SELECT 1 keeps returning one of these for all 100 iterations (e.g. the tcp_handler_fail_connection_setup failpoint returns MEMORY_LIMIT_EXCEEDED, or the server is under sustained memory pressure), the loop exhausts with server_died still unset. Under `set -u` the status.tsv write then aborts with "server_died: unbound variable", so the file is never created and ast_fuzzer_job.py:240 reports the job as ERROR: FileNotFoundError: [Errno 2] No such file or directory: '.../status.tsv' even though the server shut down cleanly (signal 15, graceful teardown, no crash/sanitizer report/core). Initialize server_died=0 before the loop, matching the existing pre-loop defaults of its two siblings (fuzzer_exit_code=0, server_exit_code=0). A truly dead server still sets server_died=1 in the else branch; defaulting to 0 only covers loop exhaustion via the "alive but busy" branches, which means the server survived. Observed over the last 30 days across 112 distinct unrelated PRs and 17 master runs, on all fuzzer variants (BuzzHouse amd_debug/arm_asan_ubsan/amd_msan/ amd_tsan and AST fuzzer). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pre-PR validation gate
Server clean-shutdown evidence (arm_asan_ubsan, sha 7cae758): Session id: cron:clickhouse-worker-slot-1:20260618-182400 |
|
cc @maxknv @Algunenano — could you review? One-line CI harness fix: |
|
Workflow [PR], commit [7c6ba5b] Summary: ✅ AI ReviewSummaryThis PR makes the fuzzer runner report Final VerdictStatus: ✅ Approve |
The previous commit initialized server_died=0 so loop exhaustion via the "alive but busy" branches no longer aborts the status.tsv write. But the TOO_MANY_SIMULTANEOUS_QUERIES branch still runs an unguarded `clickhouse-client --query "SHOW PROCESSLIST"` diagnostic under `set -e`. If the same overload that produced TOO_MANY_SIMULTANEOUS_QUERIES on SELECT 1 also rejects SHOW PROCESSLIST, that command exits nonzero and `set -e` aborts the whole script before the status.tsv write at the end of fuzz(), reintroducing the exact missing-status job ERROR this PR is meant to eliminate: FileNotFoundError: [Errno 2] No such file or directory: '.../status.tsv' Append `||:` so the diagnostic is best-effort, matching the script's existing idiom (dmesg --clear ||:, the decompress SELECT 1 ||:). The loop then continues to the next retry and the status.tsv write always runs. The sibling MEMORY_LIMIT_EXCEEDED branch only runs `sleep`, so it was already safe. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
Not required (CI fix).
Description
ci/jobs/scripts/fuzzer/run-fuzzer.shwrites the run outcome tostatus.tsvwith
echo -e "$server_died\t$server_exit_code\t$fuzzer_exit_code". Of thethree variables,
server_diedis the only one never initialized before use:it is assigned solely inside the post-fuzz server-liveness loop, either
server_died=0(SELECT 1succeeds, thenbreak) orserver_died=1(serverunreachable, then
break).Two branches of that loop,
TOO_MANY_SIMULTANEOUS_QUERIESandMEMORY_LIMIT_EXCEEDED,sleepand continue without assigningserver_diedor breaking. When
SELECT 1keeps returning one of these for all 100iterations (e.g. the
tcp_handler_fail_connection_setupfailpoint returnsMEMORY_LIMIT_EXCEEDED, or the server is under sustained memory pressure),the loop exhausts with
server_diedstill unset. Underset -uthestatus.tsvwrite then aborts withserver_died: unbound variable, so thefile is never created and
ast_fuzzer_job.py:240reports the job as ERROR:even though the server shut down cleanly (signal 15, graceful teardown, no
crash, no sanitizer report, no core dump).
Fix: initialize
server_died=0before the loop, matching the existingpre-loop defaults of its two siblings (
fuzzer_exit_code=0,server_exit_code=0). A truly dead server still setsserver_died=1in theelsebranch; defaulting to0only covers loop exhaustion via the "alivebut busy" branches, which means the server survived.
Observed over the last 30 days across 112 distinct unrelated PRs and 17 master
runs, on all fuzzer variants (BuzzHouse
amd_debug/arm_asan_ubsan/amd_msan/amd_tsanand AST fuzzer).Example CI report (BuzzHouse arm_asan_ubsan):
https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=107817&sha=7cae758e10f60706ccc862b31386f5447c67e324&name_0=PR&name_1=BuzzHouse%20%28arm_asan_ubsan%29
No related open issue found.
Version info
26.6.1.1008