GreptimeDB 跨版本兼容性测试框架实战:用 sqlness 守护升级与降级安全
GreptimeDB 跨版本兼容性测试框架实战用 sqlness 守护升级与降级安全【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb导读本文基于 GreptimeDB 仓库中的 tests/compatibility/README.md 及其配套实现系统讲解 GreptimeDB 兼容性测试框架的完整用法如何验证一个版本的 GreptimeDB 能在另一个版本写入的状态之上安全重启如何编写兼容性测试用例、控制版本匹配范围、配置旧版本 datanode 参数覆盖以及如何在 CI 中维持一个轻量的滑动版本窗口。读完本文你将能够独立编写、筛选、运行并审查 GreptimeDB 的跨版本兼容性测试并理解其底层运行原理。兼容性测试要解决什么问题GreptimeDB 是一款面向可观测性的列式数据库表数据、SST 文件、WAL、manifest 等持久化状态会随版本演进而改变编码格式与语义。当用户从旧版本升级到新版本或回滚到旧版本时新二进制必须能正确读取旧版本写下的状态——这正是兼容性测试的验证目标Compatibility tests verify that one GreptimeDB version can restart on state written by another version.测试通过cargo sqlness compat运行并复用 sqlness-runner 基础设施runner 源码位于 tests/runner/src/cmd/compat.rs。与普通 sqlness 功能测试不同兼容性测试的核心是两阶段执行setup 阶段用from版本旧版本启动集群执行建表、写入、刷盘等 SQL留下持久化状态verify 阶段用to版本新版本在同一份状态上重启集群执行查询 SQL并将输出与期望结果快照比对。只要 verify 阶段输出与期望一致即证明from → to方向的状态兼容成立反之降级场景则证明to → from方向兼容成立。快速上手六条命令覆盖典型场景原文档给出了完整的命令矩阵所有命令均通过 sqlness-runner 的compat子命令执行# 自兼容冒烟测试仅使用当前二进制 cargo run -p sqlness-runner -- compat # 从某个已发布版本测试到当前版本 cargo run -p sqlness-runner -- compat --from-version v0.9.5 # 在两个本地二进制目录之间测试 cargo run -p sqlness-runner -- compat --from-bins-dir ./bins/old --to-bins-dir ./bins/new # 从当前构建降级测试到已发布二进制 cargo run -p sqlness-runner -- compat --from-bins-dir ./bins/current --to-version v1.1.4 # 以 standalone 拓扑运行某个兼容性用例 cargo run -p sqlness-runner -- compat --topology standalone --test-filter downgrade_compatibility # 运行特定用例 cargo run -p sqlness-runner -- compat --test-filter basic_table # 仅预览将要运行的用例不启动任何服务 cargo run -p sqlness-runner -- compat --dry-run --from-version v0.9.5 # 查看全部选项 cargo run -p sqlness-runner -- compat --help从 compat.rs 的 CLI 定义可以看到除上述命令外还支持--case-dir自定义用例目录默认指向仓库根的tests/compatibility/cases、--expect-cases过滤后精确校验用例数量、--fail-fast遇错即停、--preserve-state保留临时目录中的持久化状态etcd 始终会被清理、--pull-version-on-need按需拉取不同版本二进制默认开启与--setup-etcd通过 Docker 启动 etcd默认开启。运行前提Docker用于 etcdPR1首版的分布式拓扑固定使用 Docker etcd 作为元数据存储外部元数据存储属于未来工作。From 二进制二选一——--from-version version自动拉取某个 release或--from-bins-dir path使用本地构建。注意greptime可执行文件必须直接位于给定目录内。To 二进制默认使用当前 debug 构建target/debug/greptime可通过--to-bins-dir path覆盖或用--to-version version拉取 release。自定义 target-dir如果你使用了非默认的CARGO_TARGET_DIRdebug 二进制不会位于target/debug/greptime。此时必须显式传入--from-bins-dir/--to-bins-dir指向自定义 target 目录或者在不设置自定义 target-dir 的情况下运行cargo build -p greptime。用例格式一个目录 一个兼容性场景每个兼容性用例是tests/compatibility/cases/下的一个目录当前仓库已有 29 个用例见 tests/compatibility/cases包含三个必需文件和一个期望输出文件my_case/ case.toml # 元数据必需 setup.sql # 在 from 版本上执行的 SQL必需 verify.sql # 在 to 版本上执行的 SQL必需 verify.result # verify.sql 的期望输出case.toml必需元数据name my_case reason Why this compatibility case exists introduced_by PR #1234 or feature name topologies [distributed, standalone] from_range [*] to_range [*] features [table] owner team-name # optional: namespace my_explicit_namespace # defaults to sanitized directory name必需字段name、reason、introduced_by、topologies、from_range、to_range、features、owner。以仓库真实用例 cases/basic_table/case.toml 为例它声明topologies [distributed]、from_range [*]、to_range [*]表示在所有版本组合的分布式拓扑下都验证建表/插入/改表/查询的基础兼容性。旧阶段 Datanode 配置覆盖[old_config]某些兼容性场景需要让旧版本 datanode 以特定配置运行例如复现旧版 WAL 或编码行为。此时在case.toml中加入严格的可选表[old_config] datanode old-datanode.overlay.toml规则要点只要存在[old_config]datanode字段就必需空表和未知键都会被拒绝。引用路径相对于用例目录且必须限制在该目录内。侧车文件是原生 datanode TOMLrunner 会在启动服务或创建状态之前加载并做 preflight 校验实现见 tests/runner/src/cmd/datanode_overlay.rs。合并语义runner 先应用 datanode 基线配置再合并侧车。仅当两侧值都是 table 时递归合并标量、类型不匹配、数组、表数组会原子替换基线值。特别是region_engine没有特殊合并行为。受保护字段runner 拥有的字段不允许被覆盖——mode、node_id、storage.data_home、meta_client_options.metasrv_addrs、wal.provider以及 Raft WAL 时的wal.dir或 Kafka WAL 时的wal.broker_endpoints。runner 会把这些字段恢复为基线值基线无值则删除并在不打印具体值的前提下对受保护字段的覆盖给出警告。版本范围过滤from_range/to_rangefrom_range和to_range控制用例适用于哪些二进制版本组合条目含义*匹配任意版本包括未知版本。vX.Y.Z或vX.Y.Z精确匹配 X.Y.Z。vX.Y.Z匹配 X.Y.Z 及之后。vX.Y.Z匹配严格晚于 X.Y.Z 的版本。vX.Y.Z匹配 X.Y.Z 及之前。vX.Y.Z匹配严格早于 X.Y.Z 的版本。范围列表是OR语义只要任意一个条目匹配用例就生效。尽力而为best-effort的版本推断对应实现见 compat_case.rs 中的version_matches_range与try_infer_version--from-version直接使用给定的版本--from-bins-dir/--to-bins-dir以及默认 debug 构建通过运行binary --version推断版本当版本无法确定时例如二进制缺失或--version失败非通配范围会被跳过并打印提示消息*通配范围仍然匹配。从源码看try_infer_version会执行bins_dir/greptime --version从输出典型形如greptime 0.9.5-xxxxx中提取第一个形似版本的 token 并解析为Versionversion_matches_range则逐条解析约束并做 OR 合并非法约束会被警告并跳过。示例legacy_jsonb见 cases/legacy_jsonb/case.tomlfrom_range [v1.1.0] to_range [v1.1.1]该用例仅在旧二进制 ≤ v1.1.0 且新二进制 ≥ v1.1.1 时运行——目的是验证旧二进制写下的 legacy JSONB 数据可被新版本读取且不进入 JSON2 结构化对齐路径。CI 版本窗口tests/compatibility/ci.tomlCI 任务通过 tests/compatibility/ci.toml 选择一小段滑动窗口的近期 release 作为from版本去测试 PR 构建出的to二进制from_versions [v1.1.4, v1.2.0]要点保持窗口小以控制 PR 与 merge-queue 的延迟目标是捕获从近期版本升级到最新构建的兼容性问题而不是在每个 PR 上重测所有历史版本。用例级from_range/to_range仍决定每个版本对下哪些用例运行CI 窗口只决定采样哪些旧二进制。更宽的历史窗口应放到 nightly 或 release 校验工作流中。GitHub Actions 工作流将窗口加载与 compat 调用委托给.github/scripts/run-compat.py工作流 YAML 只保留产物下载/解压与脚本调用的薄封装。downgrade_to_versions可选列出 CI 在 PR 构建集群之后重启验证的 release当前仓库配置为[v1.1.4]这些运行只选择downgrade_compatibility用例且在 distributed 与 standalone 两种拓扑下执行。setup.sql旧版本上的准备阶段在from版本集群上执行的 SQL。任何语句出错都会导致用例失败setup 输出不与任何结果文件比对。规则语句以分号结尾--前缀表示普通注释-- SQLNESS ...拦截器注释遵循普通 sqlness 语义详见下文 PR1 限制。verify.sql新版本上的验证阶段在to版本集群上执行的 SQL输出以 sqlness 快照风格与verify.result比对。verify.result期望输出快照sqlness 格式的期望输出。若文件缺失runner 会用实际输出生成该文件并判定失败——作者必须人工审查、提交生成的文件后重跑statement; output next statement; output若输出与期望不一致运行失败且verify.result会被更新为实际输出方便开发者 diff 审查。真实用例剖析升级方向basic_tablecases/basic_table/setup.sql 在旧版本上建表、插入、ALTER TABLE ADD COLUMN、再插入verify.sql 在新版本上全表查询并期望 verify.result 中 6 行数据完整返回——覆盖了 schema 演进新增列后旧数据仍可读的场景。降级方向downgrade_compatibilitycases/downgrade_compatibility/case.toml 声明from_range [v1.2.0]、to_range [v1.1.4]即验证 v1.1.4 能否重新打开由当前二进制写下的表。其 setup.sql 覆盖三类降级风险点普通表 ADMIN FLUSH_TABLE验证旧版 region WAL options 格式的兼容开启append_mode与preserve_row_sequence的追加表多次 flush ADMIN COMPACT_TABLE验证 preserve_row_sequence 语义下的行序与行数完整开启experimental_sst_float_field_encodingbyte_stream_split的浮点表验证 BSS 编码 SST 的旧版可读性。verify.result 中 4 行追加数据与 3 行浮点数据含 NULL全部按序返回证明降级后数据无损。PR1 限制首版边界Sqlness 拦截器-- SQLNESS ...注释按语句应用使用与普通 sqlness runner 相同的拦截器注册表包括 GreptimeDB 的PROTOCOL拦截器。对于PROTOCOL POSTGRES命名空间前奏使用SET search_path而非USE。另外应避免使用以pg_开头的非限定 PostgreSQL 协议表名——GreptimeDB 当前的 PostgreSQL 兼容解析器会将其改写为pg_catalog.table。分布式拓扑compat runner 启动 1 个 metasrv 3 个 datanode 1 个 frontend 1 个 flownodestandalone 兼容性运行则无需外部元数据存储。无基于注释的 compat 配置compat runner 不在 SQL 注释中定义额外的兼容性配置sqlness 注释保持其普通 sqlness 含义。命名空间隔离每个用例运行在独立的数据库命名空间中防止跨用例干扰默认命名空间由用例目录名推导清洗为[a-z][a-z0-9_]*可在case.toml中用namespace覆盖重复命名空间在发现阶段版本过滤之前即被拒绝每条语句执行前runner 执行命名空间前奏不写入verify.result通过 gRPC 执行CREATE DATABASE IF NOT EXISTS ns随后对 gRPC/MySQL 语句执行USE ns对 PostgreSQL 语句执行SET search_path TO ns。批处理行为基线先行无覆盖no-overlay的 profile 先运行。旧 datanode TOML 语义等价semantically equivalent的用例共享一个 profileprofile 串行、隔离地运行。独立生命周期每个 profile 拥有独立的状态与 etcd 生命周期其覆盖仅作用于旧阶段 datanode并在旧阶段 setup 重启期间持续生效当前阶段始终使用干净配置。严格串行用例串行执行PR1 无并行。命名空间状态是会话/协议状态无法并发共享。fail-fast 语义未开启 fail-fast 时runner 只验证 setup 成功的用例开启 fail-fast 时会在停止前清理当前活跃 profile。--dry-run仅展示选中的 profile、用例与侧车路径不打印配置值不启动任何服务。xfail 策略未来规划PR1 中所有用例都应通过。未来的 PR 将加入xfail支持且要求必填issue与expiry字段——即预期失败的用例必须关联 issue 并设定过期时间防止 xfail 被无限期搁置。跨作业分布式状态当前不支持PR1 在同一作业同一进程内完成 setup 与 verify。由于端口随机化与 etcd lease 过期问题跨作业的分布式状态 artifact 恢复在 PR1 中不受支持。这意味着分布式兼容性测试必须单进程内完成新旧二进制的状态交接。小结如何快速接入兼容性测试在tests/compatibility/cases/name/下新建case.toml声明版本范围与拓扑、setup.sql、verify.sql首次运行让 runner 生成verify.result人工审查快照后提交用cargo run -p sqlness-runner -- compat --dry-run --from-version 旧版本预览生效用例用--test-filter定向运行新用例确认通过将关键修复场景写入ci.toml的from_versions窗口保持窗口小而精。这套框架让 GreptimeDB 的每次版本演进都有据可依无论是新增编码格式、变更 WAL 选项还是调整 region 配置都可以在合并前用最小代价验证升级与降级的双向安全性。【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →