
数据库分布式数据库后端【免费下载链接】cassandraMirror of Apache Cassandra项目地址https://gitcode.com/gh_mirrors/cassandr/cassandra点击查看免费下载Harry 是 Apache Cassandra 仓库中内置的一套模糊测试fuzz testing与验证框架其核心目标是以**可复现reproducible**的方式生成尽可能贴近真实业务的读写负载同时在不打断工作负载的前提下高效地依据内置模型校验集群状态。本文基于 test/harry/main/README.md 及仓库内test/harry/main/org/apache/cassandra/harry下的源码完整讲解 Harry 的快速上手、两种工作模式、核心术语、组件架构、三种模型Checker的验证原理以及发现验证失败falsification后的调试方法论帮助你把它用起来并理解它为什么能高效地发现 Cassandra 的数据一致性缺陷。Harry 的设计目标可复现性与效率Harry 项目定位为“面向 Apache Cassandra 的模糊测试与正确性验证工具”它把模糊测试与模型校验model-based verification结合到一起。与普通的压力测试工具不同Harry 需要回答两个问题可复现性Reproducibility同一seed 同一配置无论跑多少次产生的 schema、配置与每一步工作负载都完全一致。Harry 通过使用 PCG 系列随机数生成器并让 schema、配置乃至工作负载的每一步都从可重复的随机数序列中生成来实现这一点。每个操作被分配一个单调递增的逻辑时间戳logical timestamp, lts它保证了不同次运行之间操作顺序完全一致。效率EfficiencyPCG 随机数生成器支持“在随机数序列中来回行走”walking the sequence back and forth并且值生成器被设计为保留其来源描述符descriptor的属性使得验证阶段不需要重新执行全部写入也能精确推断出期望状态。从源码结构看Harry 的入口位于 test/harry/main/org/apache/cassandra/harry其中coreConfiguration/Run、modelOpSelectors/各 Checker/Reconciler、runner各类 Runner、sut被测系统、visitors各类 Visitor共同构成了这个可插拔的验证体系仓库根目录的 ci/harry_simulation.sh 展示了如何在 CI 中用 Cassandra Simulator 驱动 Harry 进行确定性模拟。5 分钟快速开始在 JVM 内集群上跑并发读写验证Harry 可以作为一个开箱即用的“正确性压力工具”运行它会向集群写入数据并校验读到的结果与它自己记录的内容一致。README 给出了最直接的用法——在 in-JVM dtest 集群上以 2 个 Writer 线程 2 个 Reader 线程并发运行 2 分钟try (Cluster cluster builder().withNodes(3) .start()) { SchemaSpec schema new SchemaSpec(harry, test_table, asList(pk(pk1, asciiType), pk(pk1, int64Type)), asList(ck(ck1, asciiType), ck(ck1, int64Type)), asList(regularColumn(regular1, asciiType), regularColumn(regular1, int64Type)), asList(staticColumn(static1, asciiType), staticColumn(static1, int64Type))); Configuration config HarryHelper.defaultConfiguration() .setKeyspaceDdl(String.format(CREATE KEYSPACE IF NOT EXISTS %s WITH replication {class: SimpleStrategy, replication_factor: %d};, schema.keyspace, 3)) .setSUT(() - new InJvmSut(cluster)) .build(); Run run config.createRun(); concurrent(run, config, asList(pool(Writer, 2, MutatingVisitor::new), pool(Reader, 2, RandomPartitionValidator::new)), 2, TimeUnit.MINUTES) .run(); }这段代码对应着 Harry 的完整运行链路几个关键点需要理解SchemaSpec手工定义一个 schema可以包含分区键pk、聚类键ck、普通列regularColumn和静态列staticColumn。源码见 test/harry/main/org/apache/cassandra/harry/ddl/SchemaSpec.java。你也可以用SchemaGenerators随机生成 schema下文会讲到。HarryHelper.defaultConfiguration()仓库中 test/harry/main/org/apache/cassandra/harry/HarryHelper.java 提供的默认配置默认使用OffsetClock(100000)、自动创建 schema、随机生成harry.tbl_n表并配置了默认的分区/聚类描述符选择器与 DataTracker。注意默认配置没有配置 Runner会直接抛IllegalArgumentException需要像示例中这样显式指定。concurrent(...)对应Runner.concurrent工厂方法实际创建ConcurrentRunner——为每个 visitor pool 按并发度各起一个线程循环执行直到超时若有错误则合并抛出。实现见 test/harry/main/org/apache/cassandra/harry/runner/Runner.java。MutatingVisitor / RandomPartitionValidator前者执行各种写操作后者用模型校验随机分区。它们都位于 test/harry/main/org/apache/cassandra/harry/visitors 目录。运行前的初始化Runner.init会自动执行建 keyspace、建表、可选 truncate并检查表是否非空非空会打印醒目警告细节同样在 Runner.java。此外Harry 还提供了基于 YAML 配置文件的运行方式HarryRunner.loadConfig(args)读取命令行传入的配置文件参考 test/harry/main/org/apache/cassandra/harry/runner/HarryRunner.javaConfiguration.fromFile通过 Jackson YAML 反序列化出完整配置。两种工作模式单元测试模式与探索/模糊模式README 明确指出 Harry 有两种主要运行模式这也是理解整个框架的纲领单元测试模式Unit test mode由你定义具体的操作序列让 Harry 用不同的 schema 和条件去测试这些操作。通常是对集群状态施加若干写操作然后执行各种读查询并校验结果。探索/模糊模式Exploratory/fuzz mode由你定义事件的分布而不是序列本身让 Harry 不断尝试各种组合。此模式下持续向集群写入并校验状态数据量不断增长以模拟真实业务行为。两种模式共享同一套底层组件SUT、Visitor、Model、Runner区别在于操作是“预先编排的序列”还是“按分布实时生成”。在单元测试模式下Harry 使用特殊的生成式 visitorgenerating visitor把整个事件序列保存在内存中而为了效率大规模数据集的生成与验证不会使用生成式 visitor。核心术语Inflate / Deflate 与逻辑时间戳要理解 Harry必须先掌握两组核心概念Inflate / inflatable从唯一标识某个值的long描述符descriptor生产出真实值字符串、blob 等的过程。数据生成章节会详细说明。Deflate / deflatable验证阶段的反向过程——从数据库返回的值反推出它原本对应的描述符。Harry 中的形式化实体定义详见 README“Formal Relations Between Entities”一节lts逻辑时间戳由 Clock 分配、发生某个动作的时刻编号mmodification id某个lts上发生的第几个修改rts本次运行对应的近似真实时间real-timepid分区位置取值 0 到 NN 为生成的分区总数pd分区描述符唯一标识一个分区cd聚类描述符唯一标识分区内的某一行。这些实体在源码中的直接对应物是 test/harry/main/org/apache/cassandra/harry/model/OpSelectors.java 中的OpSelectors接口族PdSelector负责从lts推导pdDescriptorSelector负责从(pd, lts, opId)推导cd与各列的值描述符vd[]Clock负责lts与rts的双向映射。README 特别强调“Most of this formalization is implemented inOpSelectors, and is relied upon inPartitionVisitorand any implementation of aModel.”生成过程的层级关系是lts是决策的入口点从lts选出pd决定本次访问哪个分区对(pd, lts)组合确定#mods修改批次数与#rows每批的行数基于(pd, lts)和n所有修改批次中的操作序号选出cd操作类型、涉及的列及值由pd、lts、m、i共同决定。可复现性引擎PCG 随机数生成器Harry 选择 PCGPermuted Congruential Generator系列随机数生成器是因为除了一般 RNG 应有的良好统计特性外它还有两个对验证至关重要的特性README 原文Streams单个 seed 可以派生出多个相互独立的不同随机数流。这使得不同组件schema、分区、聚类、值、操作类型可以各自使用独立流互不干扰。Walkability可行走性PCG 生成的数流可以来回行走。给定随机数在流中的位置n可以 O(1) 拿到该位置的随机数反过来给定随机数可以确定其在流中的位置还能知道它的前一个数以及两个随机数之间隔着多少个数。前进n步是O(log(n))由于生成是循环的后退等价于前进cardinality - 1步生成 64 位熵时后退 1 步需要 64 步计算。在代码中OpSelectors.PureRng接口OpSelectors.java定义了四个核心操作randomNumber(i, stream)取随机序列stream中第i个随机数即 README 中的rng(i, stream[, e])sequenceNumber(r, stream)rng的逆运算README 中的rng(s, stream[, e])由随机数反查位置next(r, stream)/prev(r, stream)取某随机数在流中的后继/前驱。实际实现位于 test/harry/main/org/apache/cassandra/harry/gen/rngPCGFastPure、PcgRSUFast等。README 给出了一个基于滑动窗口的分区描述符选择器示例窗口大小为s、每n次迭代滑动一次先确定窗口起点再确定从s个可用pd中选哪一个每个描述符被选中n次后淘汰最旧者并补一个新描述符窗口起点与偏移量作为rng(start offset, stream)的输入保证描述符均匀分布。聚类描述符选择器类似每个分区以pd作为 stream id从大小为#cds的候选空间中取cd每个lts随机选一个offset并从此处连续取#ops个聚类超出后回绕到 0从而保证每个操作映射到唯一cd、且#op可由cd确定性反推。数据生成可逆且保序的“描述符-值”双向映射当 Harry 准备向数据库发出一条真实查询时它已经掌握了pd、cd、rts和vds[]只需把它们“inflate”成一条写语句。每个值描述符的生成/还原具备两条关键性质README 原文可逆invertible对每个inflate(vd) - value都存在deflate(value) - vd保序order-preservingcompare(vd1, vd2) compare(inflate(vd1), inflate(vd2))即两个描述符的大小关系与它们 inflate 出的值的大小关系完全一致。这两条性质对复合值如组合分区键、组合聚类键同样成立——因此inflate(pd)返回的是对象数组value[]deflate(value[])还原出pd且保序。这正是验证高效的根基模型不需要重新执行写入只要把数据库返回的行 deflate 成描述符就能与模型推导出的描述符直接比较。可以直观看到它的威力给定两个修改Update(pd1, cd1, [vd1_1, vd2_1, vd3_1], lts1)与Update(pd1, cd1, [vd1_2, vd3_2], lts2)最终结果集必然是ResultSetRow(pd1, cd1, [vd1_2rts2, vd2_1rts1, vd3_2rts2])——即同一列取逻辑时间戳更大更晚写入的值未被更新的列保留旧值。组件架构一次 Harry 运行的六个部件README 明确列出每次 Harry 运行都从 Configuration 开始。Configuration 的字段seed、schema_provider、create_schema、keyspace_ddl、clock、system_under_test、data_tracker、runner、partition_descriptor_selector、clustering_descriptor_selector等可直接从 YAML 反序列化见 test/harry/main/org/apache/cassandra/harry/core/Configuration.java。Clock负责把逻辑时间戳映射到真实时间。复现失败与验证时可以拍下 clock 快照把数据库中值携带的真实时间戳反查到写入它的操作的逻辑时间戳给定任一方向的时间戳都能算出另一个方向。实现有ApproximateClock尽量贴近真实时间、同时保留 rts↔lts 映射与OffsetClock单调、与真实时间无关位于 test/harry/main/org/apache/cassandra/harry/clock。Runner调度改变集群被测系统与模型状态的操作。包括顺序、并发、链式、分阶段四类详见下文。System under test被测系统一个 Cassandra 节点或集群默认实现是 in-JVM DTest 集群也支持外部集群。Model跟踪被测系统被告知过的逻辑时间戳并据此验证读结果。Partition descriptor selector分区描述符选择器按当前逻辑时间戳决定访问哪个分区。默认实现是一个滑动窗口在窗口内依次访问每个分区描述符重复slide_after_repeats次后淘汰最旧的分区并选入新分区。Clustering descriptor selector聚类描述符选择器决定分区内如何选聚类键——分区内最多多少行、每个逻辑时间戳访问多少行、一个批次里有多少操作、操作类型及各自出现频率。源码注释对应 OpSelectors.java。在HarryHelper.defaultConfiguration()中可以看到默认的聚类描述符选择器配置HarryHelper.javaoperationsPerLts恒为 1、maxPartitionSize为 100并为十种操作类型设置了权重——DELETE_ROW/DELETE_COLUMN/DELETE_RANGE/DELETE_SLICE/DELETE_PARTITION/DELETE_COLUMN_WITH_STATICS权重各为 1而INSERT_WITH_STATICS/INSERT/UPDATE_WITH_STATICS/UPDATE权重各为 20即写操作占绝对主导。全部十种OperationKind枚举定义在 OpSelectors.java。System Under Test 的实现README 列出四种 SUT 实现源码位于 test/harry/main/org/apache/cassandra/harry/sut实现说明injvm/InJvmSut简单的 in-JVM dtest 被测系统快速开始示例即用此println/PrintlnSut不真正执行查询而是把查询打印到 stdout适合调试mixed_in_jvm/MixedVersionInJvmSut支持混合版本集群的 in-JVM dtest 被测系统external/ExternalClusterSut面向 CCM、Docker、Kubernetes 或任何外部部署集群两个 in-JVM SUT 都具备**故障注入fault injection**能力。需要说明的是目录中还包含DoubleWritingSut、QueryModifyingSut等辅助实现。从代码结构看SystemUnderTest接口sut/SystemUnderTest.java定义了 Harry 与 Cassandra 通信的统一抽象schema 变更、查询执行、一致性级别等。README 建议SUT 是最容易自定义的组件——你只需要一种执行 Cassandra 查询的方式而 nodetool 命令、故障注入等“定制行为”目前都是用SUT/Visitor 组合实现的visitor 了解它所面对的集群内部细节。Visitors逻辑时间戳上的行为插件Visitor 定义了“在某个逻辑时间戳上触发什么行为”。README 列出的默认实现源码均在 test/harry/main/org/apache/cassandra/harry/visitorsSingleValidator对与当前逻辑时间戳关联的单个分区执行若干种不同的读查询并用给定模型校验结果AllPartitionsValidator并发校验本次运行中访问过的所有分区RepairingLocalStateValidator类似AllPartitionsValidator但在检查各节点状态前先执行repairMutatingVisitor执行各种变更写操作LoggingVisitor类似MutatingVisitor但把所有操作记录到文件便于调试CorruptingVisitor故意篡改所访问分区中的数据用于负向测试例如验证你的模型确实能发现数据不一致。README 特别强调 Visitor 必须遵守PdSelector与DescriptorSelector的规则只能对 PdSelector 为当前lts选中的分区发起变更只能访问DescriptorSelector#numberOfModifications个行操作类型必须符合#operationKind聚类与值描述符必须符合#cd与#vds。之所以如此严格是因为模型必须能精确重放施加到被测系统上的事件序列。在模糊模式下默认的分区/聚类描述符实现还支持优化验证——例如可以反查“哪些逻辑时间戳访问了与给定lts相同的分区”。Models三种校验器的工作原理Model把其余组件串起来负责判断集群返回的数据“是否合理”。它依赖 Clock把返回值的真实时间戳换算回逻辑时间戳与描述符选择器选对分区和行。README 详细讲解了三档强度的模型Visible Rows Checker可见行校验器最简单的一档逐行检查结果集中每一行是否可能由某次操作产生。它的局限是发现不了缺失的行只能捕获部分“错误覆盖”问题。它的核心思想是数据库返回的行在 deflate 状态下由pd、cd、vds[]与lts[]组成校验时从模型已知的最新操作向旧操作迭代比较每个列值描述符与模型预测是否一致。由于它只用 deflated 数据比较因此可以并发于正在进行的写操作运行。README 给出的伪代码核心逻辑为void validatePartitionState(long validationLts, ListResultSetRow rows) { long pd pdSelector.pd(validationLts, schema); for (ResultSetRow row : rows) { LongIterator rowLtsIter descendingIterator(row.lts); LongIterator modelLtsIter descendingIterator(pdSelector, validationLts); while (rowLtsIter.hasNext()) { long rowLts rowLtsIter.nextLong(); if (rowLts NO_TIMESTAMP) // 从未写入或已被删除的列无法校验 continue; while (modelLtsIter.hasNext()) { long modelLts modelLtsIter.nextLong(); if (modelLts rowLts) continue; // 模型里更新的 lts跳过 if (modelLts rowLts) throw new RuntimeException(Cant find a corresponding event id in the model); for (int col 0; col row.lts.length; col) { if (row.lts[col] ! rowLts) continue; long m descriptorSelector.modificationId(pd, row.cd, rowLts, row.vds[col], col); long vd descriptorSelector.vd(pd, row.cd, rowLts, m, col); if (vd ! row.vds[col]) throw new RuntimeException(Returned value doesnt match the model); } } } }Quiescent Checker静止校验器更强大的一档能发现任何数据不一致——错误的时间戳、缺失/多余的行、行的顺序错误等代价是不能与写操作并发——使用时必须没有 in-flight 查询所有查询在校验时刻处于确定状态。它依赖一个名为Reconciler的组件按与集群相同的顺序重放每个修改并采用 Cassandra 标准数据协调规则last-write-wins时间戳冲突时 DELETE 优先于 INSERT来 inflate 到某个lts为止的分区状态。Reconciler的实现位于 test/harry/main/org/apache/cassandra/harry/model/reconciler/Reconciler.javaREADME 指出它既是调试工具无需启动 Cassandra 集群即可得到结果集也是 quiescent 模型的核心。QuiescentChecker的源码在 test/harry/main/org/apache/cassandra/harry/model/QuiescentChecker.java。校验过程README 伪代码public void validatePartitionState(IteratorResultSetRow actual, Query query) { long maxCompleteLts tracker.maxComplete(); IteratorReconciler.RowState expected reconciler.inflatePartitionState(query.pd, maxCompleteLts, query).iterator(query.reverse); while (actual.hasNext() expected.hasNext()) { ResultSetRow actualRowState actual.next(); Reconciler.RowState expectedRowState expected.next(); if (actualRowState.cd ! expectedRowState.cd) throw new ValidationException(Found a row in the model that is not present in the resultset); if (!Arrays.equals(actualRowState.vds, expectedRowState.vds)) throw new ValidationException(Returned row state doesnt match the one predicted by the model); if (!Arrays.equals(actualRowState.lts, expectedRowState.lts)) throw new ValidationException(Timestamps in the row state dont match ones predicted by the model); } if (actual.hasNext() || expected.hasNext()) throw new ValidationException(Expected results to have the same number of results); }任何不匹配都会被立刻捕获无论是多余的行例如 Cassandra 历史上出现过的重复行 bug还是缺失的行或行内缺失的值。Exhaustive Checker穷举校验器要同时做到“与写操作并发”和“捕获所有不一致”需要第三档模型。它同样依赖 inflate 分区状态但关注每个修改的lts、opId与可见性是否仍在飞行中并遵循四条规则模型认为应该可见的操作必须可见模型认为应该不可见的操作必须不可见模型不知道状态仍在飞行中的操作可以可见也可以不可见数据库中不能存在模型不知情的状态——要么能解释一行如何产生要么判定该行错误。朴素的实现是枚举每个 in-flight 操作的可见/不可见组合但组合数指数增长。更优做法是遍历所有操作并维护“已解释操作”的状态核心数据结构README 代码public class RowValidationState { // 每个列从 UNOBSERVED 开始必须迁移到 REMOVED 或 OBSERVED private final ColumnState[] columnStates; // 记录与每个列状态相关的操作 private final Operation[] causingOperations; }校验时从最新的操作向最旧的操作遍历validateNoRow只需确认一组操作导致该行不可见——按逆序迭代若遇到未被任何写跟随的 delete 即可提前判定行不可见若存在未被 delete 跟随且未被 range tombstone 覆盖的写即为错误。validateRow则逆序迭代操作直到能解释每一列的值例如某列处于UNOBSERVED首先遇到删除该列的DELETE只需确认该列实际为null即可把状态解释为REMOVED若遇到写入了期望值的操作则判定为OBSERVED。出现看似不一致的情况时还要检查该操作是否仍在飞行中——若在飞行中其结果可能尚未可见不能可靠判定为错误。README 总结道这三种 checker 几乎都是无状态的。Exhaustive 与 Quiescent 模型只依赖DataTracker它跟踪 in-flight 与已完成的lts因为每次校验都可以从头 inflate 整个分区。当然保留少量状态仍然有用——例如只校验分区中几行时不必遍历每个访问过该分区的lts通过recordEvent记录的开始/结束修改事件可以维护pd - (cd - lts)映射VisibleRowsChecker就是这样的例子。DataTracker的实现DefaultDataTracker、LockingDataTracker位于 test/harry/main/org/apache/cassandra/harry/trackerREADME 建议QuiescentChecker与加锁的 DataTrackerLockingDataTracker配合使用。此外README 还提到两个简化模型QueryingNoOpValidator“纯跑随机查询”的 no-op 模型与QuiescentLocalStateChecker可检查每个应持有数据的副本的本地状态。Runners四种调度方式Runner 负责安排 visitor 的执行。README 列出四种实现均可在 Runner.java 中找到SequentialRunner所有 visitor顺序循环执行指定时长不同 visitor 或逻辑时间戳之间无重叠适合不需要并发读写路径的简单测试。ConcurrentRunner每个 visitor 在自己的线程中并发循环执行指定时长适合并发读写工作负载快速开始示例即此。从源码看它为每个 pool 按concurrency数创建InfiniteLoopExecutor线程等待超时后统一关闭并合并所有线程抛出的错误Runner.java。ChainRunner接收其他 runner 作为输入只执行一遍single-shot适合既包含读写负载、又包含全分区校验或其他节点级/集群级操作的简单或复杂场景。StagedRunner接收其他 runnerstage作为输入循环执行适合“读写负载后接集群变更操作”这类复杂场景。源码位于 test/harry/main/org/apache/cassandra/harry/runner/StagedRunner.java。编写单元测试从手工断言到模型化测试README 的“Writing Unit Tests”一节指出手工硬编码 schema、顺序写几条修改语句再手动断言SELECT结果对简单场景可行但无法覆盖其他 schema 或值组合下功能失效的风险。Harry 的改进思路是**用抽象的方式描述语句“类型”**而非具体语句test(new SchemaGenerators.Builder(harry) .partitionKeySpec(1, 5) .clusteringKeySpec(1, 5) .regularColumnSpec(1, 10) .generator(), historyBuilder - { historyBuilder.insert(); historyBuilder.deletePartition(); historyBuilder.deleteRowSlice(); });这段 spec 能生成不同规模、不同 schema 的集群既可在隔离条件下执行给定动作序列也可与其他随机生成的序列组合并支持故障注入。这类测试不仅保证动作序列不抛异常还能保证集群对任何允许的读查询都返回正确结果——这正是模型校验带来的额外保障。编写序列时开始描述新分区的操作要么直接调用HistoryBuilder的方法要么用#visitPartition/#beginBatch指定要访问的分区与批量动作。动作本身自解释#insert、#update、#deleteRow、#deleteColumns、#deleteRowRange、#deleteRowSlice、#deletePartition。DSL 实现位于 test/harry/main/org/apache/cassandra/harry/dslHistoryBuilder、ReplayingHistoryBuilder、BatchOperationBuilder等。HistoryBuilder生成的 history 用ReplayingVisitor重放或用同时封装两者的ReplayingHistoryBuilder后即可用任意模型默认QuiescentChecker校验查询结果查询可手工提供也可用QueryGenerator或TypedQueryGenerator生成。如果你想用更通用的“模型检查器”风格编写单元测试仓库还提供了ModelCheckertest/harry/main/org/apache/cassandra/harry/checker/ModelChecker.java以初始状态init(state)起步用step(...)注册随机挑选的步骤用invariant(...)注册每步之后必须成立的不变式并可设置beforeAll/afterAll/exitCondition最后run(maxSteps, seed)驱动执行。这与 Harry 本身的“生成式”哲学一致不写死序列而是声明状态与转移让随机性去探索。发现 falsification 之后系统化调试方法论README 坦承调试 falsification 没有“一刀切”的方案。他们曾尝试实现 shrinker但没有 Simulator 的情况下shrinker 只对非并发性质的问题有效因为否则无法制造稳定复现。以下是 README 给出的调试路径第一步判断问题是否并发相关。用相同 seed 重跑测试如果不再出现 falsification或者只是偶发、且往往发生在不同逻辑时间戳上那么问题很可能就是并发的——并发读写负载每次运行都会得到不同的读写交错。第二步若能获得顺序 runner 下的稳定复现那是最好的情况——加上日志弄清根因。即使没有稳定复现也值得按同样步骤排查检查错误本身Cassandra 返回的结果合理吗顺序有乱吗有重复或缺口吗切换到 LoggingVisitor 并仔细检查其输出同时仔细检查模型的输出这些值合理吗检查 data tracker 的输出模型或 Cassandra 是否缺失列或行输出是否包含日志中每个操作的最新逻辑时间戳in-flight 操作如何过滤相关操作日志条目并细查给定这些操作模型输出与数据库输出哪个更合理第三步缩小问题范围。根据 falsification 的表现用 Cassandra 知识判断可能相关的因素README 列举尝试更换 schema 的列类型看是否有效果尝试禁用 range delete、普通 delete 或列 delete改变分区大小看问题是否仍复现尝试禁用静态列。原则是启用/禁用给定上下文中合理的特性找到避免失败或仍能复现的最小组合。首要目标是拿到一个稳定复现——哪怕要修改 Cassandra 或 Harry或手工编排操作序列。稳定复现会让定位根因简单得多并且应该把它纳入补丁的测试套件。有时候在拿到稳定复现之前就找到了根因这种情况下仍然要产出稳定复现以简化 reviewer 的工作。最后保持耐心。调试 falsification 常常是数小时的工作问题不会总是跳到你眼前但一旦找到回报是巨大的。当前能力边界与未完成工作README 的“Features”与“Outstanding Work”两节划清了 Harry 当前的能力边界引用时务必以此为准已支持的 Cassandra 功能数据类型int8、int16、int32、int64、boolean、float、double、ascii、uuid、timestamp集合类型目前仅可 inflate不可 deflate随机 schema 生成支持任意数量的分区键与聚类键支持任意CLUSTERING ORDER BY的 schema随机生成的INSERT/UPDATE查询全列或任意列子集随机生成的DELETE查询单列、单行、行区间inflate 并验证整个分区允许 in-flight 查询inflate 并验证随机SELECT查询单行、切片单端开放、区间两端聚类键均指定。分区 inflate 由Reconciler完成分区与随机查询的验证由QuiescentChecker完成。尚未实现Unimplemented集合等部分类型不可 deflate2i二级索引查询未实现故障注入未实现可通过 Cassandra Simulator 获得TTL 不支持部分SELECT不支持LIMIT、IN、GROUP BY、token range 查询。可优化项Improvements分页已实现但硬编码为每页 1 行RNG 应能每步产出少于 64 位熵状态跟踪应改为紧凑的 off-heap 数据结构inflate 的分区状态与逐行操作日志应改为紧凑的 off-heap 数据结构关于“何时访问分区/行”的决策应改进。README 还交代了项目缘起Harry 的初衷是在 CASSANDRA-8099 大规模存储引擎重写之后驱动 Cassandra 走向稳定在数据丢失类 bug 流入生产环境之前把它们从代码库中清除下一步计划是将其集成进 CI 与常规日常开发工作流。仓库中的 ci/harry_simulation.sh 正是这一方向的体现——它通过 Cassandra Simulator 的 agent 与 bootstrap 以确定性方式运行org.apache.cassandra.simulator.test.HarrySimulatorTest测试类位于 test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java把 Harry 的模糊负载放进确定性模拟环境中执行。结语Harry 的价值与定位从整体设计看Harry 与普通模糊测试工具的关键差异在于三点用 PCG 的可行走性换取验证效率模型只需 deflate 返回值并对照描述符无需重放全部写入用逻辑时间戳保证跨运行的可复现性用可插拔的 SUT / Visitor / Model / Runner 四元组覆盖从单元测试到探索式模糊、从 in-JVM 集群到外部集群的全场景。无论你是想为 Cassandra 补丁补充模型化单元测试还是想在大规模存储引擎改动后系统性地寻找数据一致性问题Harry 提供的这套确定性验证框架都值得纳入你的工具箱。深入阅读其源码从HarryHelper、OpSelectors、Reconciler到Runner与各Checker能帮助你进一步理解每条规则背后的形式化动机。赞分享数据库分布式数据库后端【免费下载链接】cassandraMirror of Apache Cassandra项目地址https://gitcode.com/gh_mirrors/cassandr/cassandra点击查看免费下载相关推荐TimesFM 2.5从500M到200M的智能压缩实践与技术演进TimesFM 2.5从500M到200M的智能压缩实践与技术演进 问题驱动大型时间序列模型的部署困境 在时间序列预测领域我们面临着一个典型的工程困境模人工智能基础模型大模型时序预测微调autocannon配置验证工具确保性能测试场景的正确性autocannon配置验证工具确保性能测试场景的正确性 在高并发HTTP性能测试中错误的配置参数可能导致测试结果失真或系统过载。autocannon作为N性能测试测试开发工具micro正则表达式测试工具验证模式正确性micro正则表达式测试工具验证模式正确性 为什么需要正则表达式测试工具 你是否曾在终端编辑器中编写正则表达式时遇到过这些问题辛辛苦苦写的匹配模式无法正常开发工具CLI上一篇Conductor 持久化工作流生产路径从本地成功运行到可运维生产服务的完整指南下一篇如何打造个性化的沉浸式翻译界面10个实用主题定制技巧创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考