
Scylla cqlpy 测试框架指南用 Python CQL 驱动编写与运行单节点 CQL 功能测试【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb本文以 Scylla 仓库中 test/cqlpy/README.md 为核心结合 test/cqlpy/run.py、test/cqlpy/run-cassandra、test/cqlpy/conftest.py 与 test/cqlpy/util.py 等源码实现系统讲解 cqlpy 测试框架的定位、运行方式、调试手段与开发规范。读完本文你将掌握如何针对本地 Scylla/Cassandra 运行 cqlpy 测试、如何精确定位单个测试并用--count复跑、如何使用--release对比历史版本回归、如何遵循框架的 11 条规范编写高质量的新测试以及如何在缺乏本地 Cassandra 与旧版 Java 时借助 Docker 完成跨实现兼容性验证。cqlpy 是什么面向 CQL 协议的跨实现功能测试cqlpy 是 Scylla 仓库中test/cqlpy/目录下的单节点功能测试套件它的核心特点有三使用真实的 Python CQL 驱动与 pytest 框架而不是 Scylla 内部的测试接口。这意味着测试是从外部用户的视角发出 CQL 语句并断言结果与真实应用的用法完全一致可以运行在任意 CQL 实现上——既可以是 Scylla也可以是 Apache Cassandra。绝大多数测试除极少数例外应当在两者上都通过这正是 Scylla 与 Cassandra 保持 CQL 兼容性的有力保障与 Scylla 主仓库同源维护其设计初衷就是鼓励开发者在开发 CQL 功能的同时快速编写大量的功能测试详见 test/cqlpy/README.md。当前目录下测试文件覆盖了 CQL 的几乎所有主题从基础语法test_table.py 这类建表测试、test_batch.py、test_filtering.py到高级特性test_lwt.py、test_materialized_view.py、test_secondary_index.py、test_ttl.py再到 Scylla 特有的能力test_tablets.py、test_cdc.py、test_wasm.py、test_vector_index.py以及移植自 Cassandra 单元测试的cassandra_tests/子目录。快速开始对本地运行中的 Scylla 或 Cassandra 执行测试如果本机已经有一个正在运行的 Scylla 或 Cassandra监听默认的localhost:9042直接进入test/cqlpy/目录执行 pytest 即可cd test/cqlpy pytest框架通过--host与--port指定被测服务的位置通过--ssl启用加密TLSv1.2连接pytest --host 127.0.0.2 --port 9042 pytest --ssl更省事的方式是使用仓库自带的两个包装脚本它们会自动完成启动服务 → 等待就绪 → 运行测试 → 清理的全过程且被测服务运行在临时目录中测试结束后临时目录会被自动删除run自动启动 Scylla 并运行测试run-cassandra自动启动 Cassandra 并运行测试。服务可执行文件的选择run脚本会自动挑选build/*/scylla中最新的已编译 Scylla见 run.py 中的find_scylla()如果匹配到多个可执行文件而无法抉择脚本会报错并列出候选项此时需要用SCYLLA环境变量显式指定export SCYLLA/path/to/my/scylla test/cqlpy/runrun-cassandra默认使用用户PATH中的cassandra命令可用CASSANDRA环境变量覆盖少数测试在 Cassandra 上还需要nodetool可用NODETOOL覆盖。注意测试 Scylla 时完全不需要 nodetooltest/cqlpy/README.mdexport CASSANDRA$HOME/apache-cassandra-3.11.10/bin/cassandra export NODETOOL$HOME/apache-cassandra-3.11.10/bin/nodetool test/cqlpy/run-cassandra脚本内部机制临时目录、唯一 IP 与优雅清理理解run脚本的内部机制有助于排查问题时快速定位相关实现集中在 test/cqlpy/run.py。临时目录每个被测服务进程会获得一个位于$TMPDIR默认/tmp下的专属目录命名为scylla-test-pidpid_to_dir()。启动命令、生成的配置文件以及服务日志都写在该目录中其中日志文件名为log测试结束后由cleanup_all()统一处理先杀掉 pytest 子进程再对服务进程组先发SIGTERM、超时后补SIGKILLkillpg_retry()随后删除临时目录并把服务日志回显到 stdout最后打印总结如Scylla tests pass。唯一 IP为了避免与其他并发测试互相抢占端口框架利用 Linux 允许监听任意127/8网段地址的特性根据服务进程的 pid 生成一个唯一的回环地址pid_to_ip()避开127.0.*.*和127.255.255.255。因此同一台机器上并发跑多组测试也不会冲突。启动与就绪检测wait_for_services()以 0.1 秒间隔轮询一组 checker超时上限为 200 秒check_cql()用 CQL 驱动实际建连来验证 CQL 端口就绪check_rest_api()探测 10000 端口的 REST API。服务中途崩溃会被立即发现并停止重试。Scylla 的启动参数run_scylla_cmd()会为测试拉起一个专门配置的 Scylla 实例例如--developer-mode 1、--smp 2、-m 1G、--unsafe-bypass-fsync 1、--enable-tabletstrue、启用PasswordAuthenticator/CassandraAuthorizer超级用户cassandra/cassandra、显著拉长各类请求超时到 300 秒以适配慢速调试构建、并开启udf、views-with-tablets、logstor等实验特性。如果被测 Scylla 是带 sanitizer 的构建还会注入UBSAN_OPTIONS/ASAN_OPTIONS让测试在真实错误上直接失败详见 run.py 的run_scylla_cmd。选择与调试测试pytest 选项与 tier2 标记与任何 pytest 套件一样cqlpy 支持精确定位测试# 运行单个文件中的所有测试 pytest test_table.py # 运行单个测试函数 pytest test_table.py::test_create_table_unsupported_names # 重复运行 100 次需先安装扩展 pip install pytest-repeat pytest test_table.py::test_create_table_unsupported_names --count100--count100比把run脚本跑 100 次快得多Scylla 只启动一次且 pytest-repeat 会替你统计失败的次数。两个最常用的调试选项-v显示每个测试的名称默认只显示进度点-s显示测试的完整输出默认 pytest 会捕获输出仅在失败时展示。此外从源码看run_pytest()默认会给 pytest 追加-m not tier2参数即默认跳过标记为tier2的测试以保持本地开发和 CI 每次运行聚焦且快速如果你显式传入-m表达式则会使用你自己的表达式见 run.py 的_prepare_pytest_args()。作为示例--release一节用到的回归测试位于 test_prepare.pypytest test_prepare.py::test_duplicate_named_bind_marker_prepared用--release对历史版本做回归对比run脚本支持下载 ScyllaDB 官方发布的预编译 Scylla 版本并对其运行测试从而演示某个测试在不同发布版本间的回归test/cqlpy/run --release 2022.1 --runxfail \ test_prepare.py::test_duplicate_named_bind_marker_prepared test/cqlpy/run --release 2022.2 --runxfail \ test_prepare.py::test_duplicate_named_bind_marker_prepared例如上面的两条命令可以展示该测试在 Enterprise 2022.1 与 2022.2 之间出现的回归。--release必须是传给run的第一个选项脚本会下载对应官方发布版并缓存到build/目录如build/2021.1.9然后针对该版本运行指定的测试。--release支持多种版本说明符下载逻辑见 fetch_scylla.py说明符含义5.4.7精确版本5.45.4 分支的最新版本例如 5.4.75.4.0~rc2预发布版本2021.1.9Enterprise 精确版本2023.1Enterprise 分支最新版本底层实现上download_scylla()通过匿名unsignedS3 请求从downloads.scylladb.com下载relocatable打包仅含 Scylla 可执行文件与所需共享库解压到build/release后生成一个名为scylla_wrapper的 shell 包装脚本设置LD_LIBRARY_PATH后经由libreloc/ld.so启动。三段的精确版本号如5.4.7无需联网查询即可判断是否已缓存两段的分支号如5.4则会查询 S3 以确认是否存在更新版本。需要注意run.py的run_precompiled_scylla_cmd()会按版本号硬编码地增删命令行参数如较旧版本不支持--enable-tablets、--maintenance-socket等以兼容历史版本的启动方式。编写新 cqlpy 测试的 11 条原则cqlpy 被放在 Scylla 主仓库而非外部独立仓库正是为了服务三个目标易于编写新测试、易于理解失败原因、易于快速反复运行。同时能够对 Cassandra 运行同一套测试也让开发者可以在实现功能之前就写出正确的测试即测试驱动开发。为维持这些优势README 给出了 11 条编写新测试时应遵循的原则保持每个测试快速理想情况下每个测试函数耗时不足 1 秒。撰写本文档时整个套件 800 多个测试函数约 80 秒跑完平均每个测试约 0.1 秒。写测试前请反问自己真的需要插入一百万条数据或 sleep 5 秒吗通常不需要。短测试让你在开发时乐于反复运行单个测试也让大家敢于在开发中跑完整套件而非靠猜。保持每个测试小巧不要为某个特性的多个方面写一个大测试函数而是在同一文件中写多个小函数每个覆盖一个方面。这样失败时能精确指出特性中坏掉的部分代码也更易读。使用 fixtures 减少测试时间当许多小测试需要相同的公共 setup如同构表或固定数据时使用 pytest fixture 让多个测试共享同一个临时表而不是各自重建。共享表时务必用唯一 key而非硬编码 key以免其他测试意外撞键。README 指出test/alternator 比 test/cqlpy 快平均每个测试 0.03 秒 vs 0.1 秒的重要原因就是它更善用 fixtures、极少自建表——这是我们应该努力靠拢的目标虽然 CQL 中不同 schema 需要不同表更难做到。写注释自解释的测试名远远不够。一年后测试失败时没人记得你当初测的是什么、为什么检查这些特定条件或者这是否是一个修复的 backport 以及对应哪个 issue。每个测试函数前请解释它为何存在、要验证什么特性、为何采用这种特定方式若用于复现某个 issue请给出 issue 编号。对 Cassandra 运行你的测试仅对 Scylla 跑通不够还要用test/cqlpy/run-cassandra对 Cassandra 跑一遍。Scylla 专属特性可用scylla_onlyfixture 在 Cassandra 上跳过但 Scylla 绝大多数 CQL 特性与 Cassandra 一致因此大多数测试应当在 Cassandra 上通过。若在 Cassandra 上失败多半是测试本身写错了应修复测试极少数情况下是 Cassandra 的已知 bug 或 Scylla 与 Cassandra 的刻意差异此时用cassandra_bugfixture 标记 xfail但必须用注释说明原因链接 Cassandra issue或 Scylla 中决定偏离 Cassandra 实现的 issue或用文字解释差异。考虑风险用例不要随机化开发者本人边开发边写测试的好处就是能针对特定边界条件编写测试。例如某个操作接收字符串如果你知道空字符串或超长字符串需要特殊代码、有被误处理或在重构中被破坏的风险就应为它们各写一个测试。反之用 1000 次随机长度为 10 的字符串循环既慢又会漏掉空串和超长串这些有趣用例它们几乎不可能被随机抽到还容易让评审者误以为空串已被覆盖。模糊fuzz测试自有其价值但在 cqlpy 语境下几乎总是错误选择应放到独立框架或至少独立文件中。写测试而不是测试库不要沉迷于把代码抽取成工具函数。确实已有少量工具函数放在 util.py、nodetool.py 和 rest_api.py 中但请克制继续增加的冲动工具函数让测试更难读读者都懂cql.execute(...)却不熟悉几十个晦涩的辅助函数为单个测试写的工具函数往往远不如作者想象得通用复用时要么复制要么修改最终导致一堆令人困惑的相似函数。若你认为某段代码值得抽取先把它放在需要它的单个测试文件内只有当多个测试文件都能受益时才上移到 util.py。撰写本文档时cqlpy 有 2 万多行测试代码、约 500 行库代码——请保持这个比例我们在写测试集合而非库。不要过度设计延续上一条请聚焦于让单个测试易于编写、易于阅读。不要用类、强类型等大项目特性来设计测试套件——把测试放进类里如 dtest 那样会让运行单个测试必须指定类名徒增麻烦。测试文件本身已足够用于在测试间共享函数。把测试放到正确的文件将验证同一特性、主题相近、或共用某 fixture/便利函数的测试尽量放在同一文件中。新增小测试文件没有额外开销不像 C 测试每个文件编译有固定开销但文件过多会带来认知负担。写新测试时先考虑它是否契合已有文件主题若确需新建文件在注释中说明未来哪些测试可能归入该文件。测试用户可见的 CQL 特性通常但不绝对应优先测试用户通过 CQL 驱动能触达的特性。我们确实有检查日志消息、trace 等的测试但应占少数大多数测试不应检查对 CQL 应用不可见的日志。检查错误条件的测试应当断言错误的类型与关键子串而非完整错误消息——否则每次修改错误消息的细枝末节几十个测试就会跟着挂掉。不要动cassandra_testscassandra_tests/子目录中的测试是从 Cassandra 单元测试翻译而来使用轻量兼容层 cassandra_tests/porting.py 简化翻译参见其目录内 README目录组织镜像 Cassandra 的test/unit/org/apache/cassandra/cql3文件名由SomeThingTest.java改为some_thing_test.py每个文件都注明翻译自哪个版本。若非在继续翻译更多 Cassandra 测试请避免修改该目录、不要往其中任何文件添加新测试。新测试一律放到 cqlpy 目录中除cassandra_tests子目录以外的任何位置。支撑上述原则的 fixtures 与工具函数源码佐证原则 3、5 等在实践中落地为 conftest.py 中定义的一组 pytest fixturescql核心会话 fixture默认使用cassandra/cassandra超级用户凭据对 Scylla 与 Cassandra 均有效连接--host/--port指定的服务连接失败会立即以内部错误退出而非让每个测试各自失败cql_test_connection函数级 autouse fixture在每个测试后执行一条BEGIN BATCH APPLY BATCH探活语句一旦发现连接中断即判定 Scylla 崩溃并终止后续测试避免在死掉的服务上继续报错scylla_only/cassandra_bug通过查询system_schema.tables中是否存在名字含 scylla 的系统表来区分 Scylla 与 Cassandra见 util.py 的is_scylla()scylla_only在 Cassandra 上跳过测试cassandra_bug在 Cassandra 上标记 xfailtest_keyspace/test_keyspace_vnodes/test_keyspace_tablets创建并自动清理临时 keyspaceRF1可参数化选择 vnodes 或 tablets 复制模式random_seed测试用到random模块时使用该 fixture种子会在失败时打印出来便于复现测试结束后恢复随机状态scylla_path通过解析/proc/net/tcp找到监听 CQL 端口的本地进程再以/proc/pid/exe定位 Scylla 可执行文件供需要调用 Scylla 工具的测试使用其他还有driver_bug_1跳过旧版 Python 驱动的空页 bug、compact_storage、skip_s3_tests等。util.py 则提供了一批轻量工具函数unique_name()生成唯一表/keyspace 名new_test_keyspace/new_test_table/new_type/new_function/new_aggregate/new_materialized_view/new_secondary_index提供with上下文管理器风格的临时对象创建与自动清理——其中new_test_table还会复用已删除的表名避免海量建删表拖慢 Scyllanew_session/new_cql用于需要独立连接的测试。另外nodetool.py 为测试提供flush、compact、take_snapshot、list_snapshots、enablebinary、setlogginglevel等 nodetool 兼容操作对本地 Scylla 优先走 10000 端口 REST API无需外部 Java/JMX 进程仅在 Cassandra 或远程场景下回退到外部nodetool命令。安装 Cassandra跨实现兼容性验证的前提对 Cassandra 运行 cqlpy 测试有助于写出正确测试、保障兼容性甚至在开发特性之前先写测试TDD。由于现代 Linux 发行版普遍移除了cassandra软件包README 提供了几种安装与运行 Cassandra 的方式run-cassandra脚本会自动处理 Java 版本选择。Java 要求建议系统上装有Java 8 或 11二者任选其一作为次要 Java 即可不必是默认 Java。run-cassandra会从多个已装版本中自动挑选合适的 Java其find_java()依次探测/usr/lib/jvm/jre-11/bin/java、/usr/lib/jvm/jre-1.8.0/bin/java、java要求主版本为 8 或 11见 run-cassandra。脚本可以抗议式地在 Java 21 上运行 Cassandra 5需设置CASSANDRA_JDK_UNSUPPORTEDtrue脚本已自动处理但 Cassandra 3/4 不行。在 Fedora 41 及更早版本上安装 Java 11 作为次要 Javadnf install java-11Fedora 42 需要从 Fedora 41 仓库安装dnf install --releasever41 java-11-openjdk-headless.x86_64方式一推荐--docker连 Cassandra 带 Java 一起容器化--docker[CASSANDRA_VERSION]使用官方cassandraDocker 镜像镜像内同时打包了 Cassandra 与合适的 Java 版本本机完全无需安装 Cassandra。每次测试的配置与数据写入$TMPDIR默认/tmp的子目录并 bind-mount 进容器。由于官方cassandra:4.1与cassandra:5镜像共享 Ubuntu 与 Java 基础层无论测试多少个 Cassandra 版本基础层都只需下载一次。test/cqlpy/run-cassandra --docker # 默认是最新 5 系列 test/cqlpy/run-cassandra --docker4.1 # 4.1 分支最新补丁版 test/cqlpy/run-cassandra --docker4.1.11 # 精确补丁版本 test/cqlpy/run-cassandra --docker3.11少数测试会调用nodetool使用--docker时 nodetool 也自动取自 Docker 镜像无需本机安装或设置NODETOOL脚本会把NODETOOL设置为docker run --rm --network host cassandra:版本 nodetool。前提是 Docker 已安装并运行首次运行会自动拉取镜像并缓存。方式二--java-docker本地 Cassandra 容器化 Java--java-docker[JAVA_VERSION]适用于本机已有 Cassandra 安装、但缺少合适 Java 的场景本地 Cassandra 安装目录被 bind-mount 进一个提供指定 Java 版本的容器Cassandra 启动脚本与 Java 都在该容器内运行。默认 Java 版本为 11export CASSANDRA/tmp/apache-cassandra-4.1.4/bin/cassandra test/cqlpy/run-cassandra --java-docker # 默认 Java 11 test/cqlpy/run-cassandra --java-docker11--docker与--java-docker不能同时使用脚本会报错退出。两种 Docker 模式都要求 Docker 已安装并运行。方式三使用预编译 Cassandra前往 Cassandra 官方下载页下载bin.tar.gz包例如 4.1.4解压到任意目录即可无需安装到特定位置tar zxvf apache-cassandra-4.1.4-bin.tar.gz解压目录中就有bin/cassandra与bin/nodetool交给run-cassandra使用export CASSANDRA/tmp/apache-cassandra-4.1.4/bin/cassandra export NODETOOL/tmp/apache-cassandra-4.1.4/bin/nodetool test/cqlpy/run-cassandra testfile.py::testfunc方式四从源码构建 Cassandra当需要测试非官方或修改版 Cassandra 时可以自行构建。先获取 Cassandra 源码再使用 Java 11 构建以下命令假设 Java 11 位于 Fedora 的常见路径JAVA_HOME/usr/lib/jvm/java-11 JRE_HOME/usr/lib/jvm/java-11/jre \ PATH$JAVA_HOME:$JRE_HOME/bin:$PATH CASSANDRA_USE_JDK11true \ ant -Duse.jdk11true构建需几分钟并可能首次向 maven 缓存$HOME/.m2下载大量 JAR 依赖。构建完成后源码目录中即出现bin/cassandra、bin/nodetool按方式三的方式交给run-cassandra即可export CASSANDRA/tmp/cassandra/bin/cassandra export NODETOOL/tmp/cassandra/bin/nodetool test/cqlpy/run-cassandra testfile.py::testfuncrun-cassandra 如何生成 Cassandra 配置从源码看run-cassandra 的run_cassandra_cmd()脚本会在临时目录生成一份完整的conf/cassandra.yaml包括数据/commitlog/hints 目录指向临时目录、Murmur3Partitioner、SimpleSnitch、本机唯一 IP 作为 seed 与listen_address、PasswordAuthenticator/CassandraAuthorizer、启用 SASI 索引与物化视图、关闭auto_snapshot等同时按 JVM 版本写出jvm11-server.options/jvm17-server.optionsJPMS 相关--add-exports/--add-opens参数其中一份留空以满足启动脚本的 grep 检查并设置JVM_OPTS让 JMX 监听 7199 端口以便测试使用 nodetool。也就是说你不需要学习如何手工运行 Cassandra——run-cassandra会替你完成一切。结语把 cqlpy 当作 CQL 兼容性的守门员cqlpy 的独特价值在于一套测试、两个实现它既是 Scylla CQL 功能的回归防线又是与 Cassandra 行为对齐的兼容性标尺。无论你是想快速验证本地构建、对比历史发布版本的行为差异还是为即将开发的新特性提前编写测试都可以按本文的路径直接上手。进一步阅读的入口运行与开发总纲test/cqlpy/README.md启动/清理 Scylla 与测试编排test/cqlpy/run.py、test/cqlpy/run启动/清理 Cassandra 与 Docker 模式test/cqlpy/run-cassandra历史版本下载test/cqlpy/fetch_scylla.py公共 fixturestest/cqlpy/conftest.py工具函数test/cqlpy/util.py、test/cqlpy/nodetool.py移植自 Cassandra 的测试test/cqlpy/cassandra_tests/【免费下载链接】scylladbNoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB项目地址: https://gitcode.com/GitHub_Trending/sc/scylladb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考