ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ClickHouse 无状态测试(Stateless Tests)实战指南:clickhouse-test 的运行机制、常用参数与用例编写

ClickHouse 无状态测试(Stateless Tests)实战指南:clickhouse-test 的运行机制、常用参数与用例编写 ClickHouse 无状态测试Stateless Tests实战指南clickhouse-test 的运行机制、常用参数与用例编写【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouseClickHouse 的无状态测试Stateless Tests是一套面向 SQL 行为回归验证的核心测试体系它不依赖预先灌入的外部数据每个测试独立建临时库、执行查询并把标准输出与.reference期望文件逐字节比对。本文以仓库内.claude/instructions.md记录的开发流程为主线结合 测试运行器tests/clickhouse-test的真实源码系统讲解从构建、启动服务到运行、排查 stateless 测试的完整闭环读完即可在本仓库本地环境复现 CI 同款的功能测试能力。一、Stateless Tests 是什么先理解这套测试的定位在 ClickHouse 仓库中无状态测试用例集中存放在tests/queries/0_stateless/仓库内共有一万余个测试文件编号如00001_select_1.sql。所谓「无状态」指每个用例在运行前不依赖任何预设的持久化业务数据——运行器会为每次执行创建临时数据库与临时表测试结束时环境即被清空因此用例彼此隔离、可并行、可重复。执行这些用例的统一入口是根目录下的 Python 运行器tests/clickhouse-test仓库内约 7300 行由#!/usr/bin/env python3声明启动。它与 CI 中的功能测试任务如ci/jobs/functional_tests.py共用一套逻辑也就是说你本地跑通的命令就是 CI 里 stateless 任务实际执行的核心流程。二、运行前置条件构建、启动服务与就绪探测.claude/instructions.md给出了最简三段式准备流程每一步都有明确目的# 1. 构建 ClickHouse 服务端与客户端二进制 cd build ninja clickhouse # 2. 用仓库自带的服务端配置启动服务 ./build/programs/clickhouse server --config-file ./programs/server/config.xml # 3. 用一次最小查询探测服务是否就绪 ./build/programs/clickhouse client -q SELECT 1第 1 步假设build/目录已完成 CMake 配置全新检出时需要先执行cmake -S . -B build ...之类的一次性配置之后每次增量构建只需ninja clickhouse。第 2 步加载的配置文件programs/server/config.xml即发行版默认服务端配置。按该文档说明默认监听 TCP 端口为9000原生客户端协议、HTTP 端口为8123HTTP 接口——这两个默认值随后会与测试运行时的端口环境变量严格对应是很多「测试连不上服务」问题的根因。测试场景下更贴近 CI 的另一种做法是使用tests/config/目录下的专用测试配置但本地快速验证时直接使用programs/server/config.xml即可。第 3 步的client -q SELECT 1本质是一次就绪探针liveness probe只有服务端完成表加载、能接受查询后客户端才会返回结果并正常退出此时才能开始跑测试。三、运行测试端口环境变量为何必须设置运行器通过以下命令执行测试测试名可传目录名或单个用例名CLICKHOUSE_PORT_TCP9000 CLICKHOUSE_PORT_HTTP8123 ./tests/clickhouse-test test_name两个环境变量的作用可在 tests/clickhouse-test 中得到印证。运行器启动时会读取它们并写入客户端连接配置相关代码位于该文件约 L3829-L3863 与 L7204-L7221若未通过环境变量显式指定运行器会回退到命令行参数--tcp-port/--http-port的逻辑。除基本两件套外源码中还支持CLICKHOUSE_PORT_TCP_SECURE、CLICKHOUSE_PORT_HTTPS、CLICKHOUSE_PORT_HTTP_PROTO用于把 HTTP 切到https等加密通道变量——当使用 TLS 服务配置时可据此补齐。值得注意的细节是如果手动修改了服务端监听端口却没有同步设置环境变量运行器会按默认值9000/8123探测导致「服务明明在跑却全部超时」。因此实践上建议把端口统一收敛到上述默认值或在命令行显式覆盖例如./tests/clickhouse-test -- --help # 查看运行器全部选项 CLICKHOUSE_PORT_TCP9001 ./tests/clickhouse-test 00001_select_1 # 端口与默认不同时四、常用 Flags 速查与源码依据.claude/instructions.md重点介绍三个在本地调试与 CI 排障中最高频的选项它们都实现在 tests/clickhouse-test 的参数解析段约 L6680-L6760Flag作用典型使用场景--no-random-settings禁用「随机化 settings」机制排除某个SET随机值恰好触发失败的干扰做确定性复现--no-random-merge-tree-settings禁用 MergeTree 表引擎相关设置的随机化排查 MergeTree 行为差异或配合随机化诊断定位问题--recordstdout 与期望不一致时自动把 stdout 写回.reference文件验证行为变更后批量刷新期望输出关键提醒--record的副作用源码在 L7104-L7109 处特意注明——开启--record时运行器会禁用引擎替换engine replacement以确保录制的.reference是测试原始输出。这意味着你录下来的结果可能与开启了Replicated引擎替换等「云模式改写」的 CI 环境不一致。因此--record更适合本地比对、确认新输出符合预期后再由开发者审阅提交而非盲目全量刷新。除文档点名的三个选项外从同一段参数解析代码还能看到运行器还支持--skip跳过指定测试、--no-stateful关闭有状态测试、--no-long跳过long标签用例、--sequential、--client-option追加客户端参数、--s3-storage/--azure-blob-storage在对象存储后端上跑、--distributed-cache以及一组用于复现随机化失败的--random-settings-diagnostics-dir/--diagnose-random-settings。本地调试建议组合使用CLICKHOUSE_PORT_TCP9000 CLICKHOUSE_PORT_HTTP8123 \ ./tests/clickhouse-test 00001_select_1 \ --no-random-settings --no-random-merge-tree-settings五、测试文件扩展名生态与.reference比对机制Stateless 测试不止是.sql。.claude/instructions.md归纳了完整的文件类型体系仓库中这些类型都能在tests/queries/0_stateless/中实际找到对应文件扩展名含义.sql最常用的纯 SQL 测试.sql.j2基于 Jinja2 模板的 SQL 测试可参数化渲染后执行.shShell 脚本型测试用于需要外部进程配合的场景.pyPython 测试.expectExpect 脚本测试交互式会话驱动.reference期望输出文件测试 stdout 与之逐字节比对.gen.reference.j2模板测试运行前生成的期望文件运行器的核心判定逻辑是执行用例后捕获 stdout/stderr与同目录同名.reference做 diff不一致即失败。一个最朴素的用例是tests/queries/0_stateless/00001_select_1.sql内容仅SELECT 1其对应期望文件即00001_select_1.reference。同时普通 SQL 测试还会用到行内错误断言注释。例如 tests/queries/0_stateless/00002_system_numbers.sql 中select x from system.numbers limit 1; -- { serverError UNKNOWN_IDENTIFIER }表示该语句期望抛错且错误码必须是UNKNOWN_IDENTIFIER——这类注释同样会被运行器解析并参与结果判定是编写「验证报错行为」用例的基础语法。六、数据库名归一化.reference里为什么必须写default这是 stateless 测试最容易踩坑、也最容易被误解的机制。.claude/instructions.md明确指出运行器会为每个测试创建名字随机的一次性临时数据库形如test_abc123以便并行执行时互不干扰而执行结束后运行器在比对前会把 stdout/stderr 中出现该随机库名的位置统一替换成default再与.reference比较。这一行为在 tests/clickhouse-test 中有直接代码佐证测试输出文件经由 L3816-L3818 的replace_in_file(self.stdout_file, database, default)stderr 同理完成归一化而随机库名会通过环境变量CLICKHOUSE_DATABASE见 L2305、L2334与--param_CLICKHOUSE_DATABASE见 L3745-L3750注入执行环境。由此得出一个硬性规则编写或修改.reference时库名一律写成default绝不能写${CLICKHOUSE_DATABASE}或某个真实随机名——否则归一化前后永远对不上用例必然误报失败。同理期望输出里也不要依赖任何与随机库相关的副作用。七、Tags用首行注释控制用例执行策略用例可以通过文件内首行注释打标签声明自身的行为约束语法为-- Tags: no-fasttest, no-parallel运行器在 read_test_tags_and_random_settings_limits 附近解析这些标签前缀Tags:并在调度与随机化阶段消费它们。常见标签及含义如下Tag含义disabled用例当前被禁用不参与执行no-fasttest不进入 fasttest 快速冒烟集no-parallel该用例禁止与其他用例并行no-random-settings对应用例自身要求不参与 settings 随机化no-random-merge-tree-settings对应用例要求不参与 MergeTree 设置随机化long耗时长的用例配合--no-long可跳过注意标签语义与第三节全局 flag 的对应关系--no-random-settings是全局关闭随机化而用例上的no-random-settings标签是单测豁免——两者粒度不同排障时可先看是哪个层级在生效。八、Random settings limits给随机化画上范围ClickHouse 在 CI 中默认会为每个测试随机挑选一部分SET参数如max_threads来扩大覆盖、暴露竞态代价是结果不稳定。为了既保留随机化、又约束到合理范围.claude/instructions.md给出了行级限制注释的写法-- Random settings limits: max_threads(1, 4); max_block_size(100, 1000)运行器按分号拆分每条限制、用 Pythonliteral_eval风格解析(min, max)元组再在生成随机值时把对应 setting 钳制在该闭区间内核心逻辑见 parse_random_settings_limits_from_line 与 apply_random_settings_limits。典型应用是某用例在max_threads取大值时偶发失败你既不想全局关掉随机化又需要锁定线程数做复现这时就在文件里声明max_threads(1, 4)之类的小范围。九、收尾如何正确停止服务本地调试结束后.claude/instructions.md建议用进程管理命令优雅结束服务不要在测试运行中途直接杀进程否则可能残留临时表或锁pgrep -f clickhouse server # 先拿到服务进程 PID 列表 kill pid1 pid2 # 再逐个停止运行器本身对进程也做了配套设计例如clickhouse-test开头L58-L66就提到 CI 侧可用--cleanup清理被SIGKILL打断后遗留的孤儿进程组PID 记录写入ci/tmp/clickhouse_test_group_pid.pid。因此在本地多开服务时务必用pgrep -f clickhouse server精确匹配、避免误杀其他实例。十、实践小结与延伸阅读把整条链路串起来一次标准化的 stateless 本地调试流程是# 1. 准备首次需先 CMake 配置 build 目录 cd build ninja clickhouse ./build/programs/clickhouse server --config-file ./programs/server/config.xml ./build/programs/clickhouse client -q SELECT 1 # 就绪探测 # 2. 单测 确定性调试 CLICKHOUSE_PORT_TCP9000 CLICKHOUSE_PORT_HTTP8123 ./tests/clickhouse-test 00001_select_1 \ --no-random-settings --no-random-merge-tree-settings # 3. 结果异常时审视三处而不是盲改 # - 用例自身-- { serverError ... } 行内断言 # - .reference库名是否遵守 default 归一化规则 # - Tags / Random settings limits 注释如需继续深入可在当前仓库中查看以下材料运行器全量逻辑见 tests/clickhouse-test海量真实用例见 tests/queries/0_stateless/进程与超时管理的最佳实践见 tests/clickhouse-test-process-management.mdCI 侧的调度封装见 ci/jobs/functional_tests.py。理解本节第三、六、七部分三个「隐性规则」端口注入、库名归一化、标签粒度就能让本地调试行为与 CI 判定保持一致避免「本地过了、CI 红了」的经典困惑。【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进