moonsim

Deterministic simulation and model testing toolkit for MoonBit.

moonbit
simulation
deterministic
model-testing
replay
moon add zlhahaha/moonsim@0.3.1
Download zip
Author
Version
0.3.1
License
Apache-2.0
Last updated
last month
Downloads
12
README

#moonsim

MoonBit 的通用确定性事件模拟与模型测试框架。

moonsim 用同一套虚拟时间、固定 seed、可控变异、invariant、稳定 trace digest 和失败重放能力,测试消息、队列、任务编排、定时器、状态机与外部调用。它适合把偶发的延迟、丢失、重复、乱序和失败变成可复制的模型测试,并将修复策略固化为回归用例。

moonsim 可为消息可靠性模型和任务编排模型生成有序、可校验、可重放的事件证据。模型既可独立运行,也可接收适配器记录的外部行为,让同一套 invariant 同时服务于设计验证、故障复现和 CI 回归。

#为什么需要 moonsim

分布式业务里最难复现的错误,往往不是某个函数算错,而是多个正确操作以意外顺序组合:确认包延迟导致重复投递、超时与成功同时发生、任务依赖尚未完成却被调度,或终态在重试后发生回退。真实时间测试通常运行慢且容易抖动;普通 mock 能替换依赖,却很难系统表达时间、因果关系和故障组合。

moonsim 的特殊价值是把这些条件压缩成一个可保存、可比较的确定性实验:

  • 虚拟时间:无需等待真实秒数,就能验证 timeout、backoff、截止时间(deadline)和定时任务。
  • 统一事件语言:消息、任务、定时器、状态转移和外部调用共享 tick、因果关系、correlation_id 与 trace。
  • seed 驱动的故障空间:在输入、seed 和变异策略相同时,延迟、丢弃、重复、同 tick 乱序和失败注入的执行结果可以稳定复现,而不是依赖偶发竞态。
  • 失败结果附带证据:每个失败结果都包含 seed、触发的 invariant、事件证据与 digest,可直接 replay,也能固化为修复后的回归用例。
  • 模型与基础设施解耦:同一业务规则可以先在纯模型中快速探索,再由适配器接入记录下来的外部行为;核心不绑定 HTTP、数据库或 MQ 客户端。
  • MoonBit 原生公共 API:模型、示例、测试与报告均可直接参与 MoonBit 项目的 moon checkmoon test 和 CI,不需要跨语言测试进程。

它把“事件何时发生、以什么顺序发生、失败后系统是否仍满足规则”变成可编程、可重复执行的测试对象。

#与其他语言工具的关系

moonsim 吸收了多个成熟工具方向中适合软件可靠性测试的能力,并在 MoonBit 中组合成统一闭环:确定性离散事件、故障变异、业务 invariant、稳定摘要、失败重放和 CI 集成。下表用于说明能力定位和设计思想来源,不表示 API 兼容,也不表示完整复刻或替代对应项目。

工具或方向moonsim 对应提供的能力moonsim 的组合增强
Akka TestKit消息探测、时序断言和失败路径验证同一事件流还能表达任务、定时器、状态转移与外部调用,并保留跨组件因果关系
Python SimPy虚拟时间、事件调度和确定性执行面向软件可靠性直接提供 seed 变异、invariant、trace digest 与失败重放,无需自行拼装测试闭环
Java DESMO-JCloudSim离散事件排序、调度和容量场景运行以轻量 MoonBit API 聚焦消息、工作流和状态机,可直接进入 moon test 与现有 CI
Haskell QuickCheck、Python Hypothesis用属性或 invariant 判断大量执行结果失败证据同时保存 seed、策略、关键事件和 digest,可在相同输入、配置和代码版本下确定性 replay
Jepsen 风格模型验证通过历史事件检查系统规则是否成立在纯模型中快速枚举延迟、丢弃、重复、乱序和失败组合,适合在开发与 CI 阶段高频回归
手写 mock、fake clock替换依赖、控制时间和构造异常返回把时间、五类事件、因果关系、变异、证据与 replay 做成公共框架,减少重复测试基础设施

这些能力在其他生态中通常分散于模拟器、属性测试、消息测试工具和故障验证系统;moonsim 的优势是用一套类型化事件 API 把它们连起来。一次失败可以从变异策略进入 invariant,再生成 digest 与事件证据,最后按同一 seed 重放并固化为回归测试。moonsim 为 MoonBit 项目提供了这套开箱可用的组合能力,减少项目重复搭建测试基础设施的成本。

#安装与最小示例

下面的步骤从空目录创建独立项目,并使用 mooncakes.io 上的 0.3.1,不依赖本仓库源码:

moon new moonsim-consumer Set-Location moonsim-consumer moon add zlhahaha/moonsim@0.3.1

cmd/main/moon.pkg 中声明根包依赖:

import {
"zlhahaha/moonsim",
}

cmd/main/main.mbt 替换为以下内容。依赖已经在 moon.pkg 中声明,因此 MoonBit 源文件不需要、也不能再写 import

fn main {
let stream = @moonsim.event_stream()
let sent = stream.record(
@moonsim.message_event_kind(), 0, "message.send",
correlation_id="order-42", source="producer", target="worker",
)
ignore(stream.record(
@moonsim.task_event_kind(), 3, "task.complete",
correlation_id="order-42", parent_id=sent.id,
))

let policy = @moonsim.event_mutation_policy(
seed=2026UL, duplicate_percent=100,
)
let result = stream.replay(policy)
let failure = @moonsim.event_failure_case(
"order_completed_once", stream, policy,
)
let replay = failure.replay()
println("failed_rule=" + failure.rule)
println("digest=" + result.digest.to_string())
println("same_seed=" + result.matches_digest(replay).to_string())
}

运行:

moon check --deny-warn moon run cmd/main

输出中的 digest 是稳定整数;关键结果应为:

failed_rule=order_completed_once same_seed=true

这个最小示例演示事件流、变异策略、失败证据和重放接口。业务 invariant 可以根据 EventReplayResult.events 检查最终分类次数、确认前是否丢失、重试上限、依赖顺序与终态保护;完整的 invariant 检查流程见队列与工作流示例。框架同时提供事件结构自身的因果与时间检查。

#稳定事件类型

  • Message:发送、投递、确认、重试、死信等消息事实。
  • Task:任务就绪、开始、完成、失败、取消与依赖阻塞。
  • Timer:超时、定时触发、退避和 deadline。
  • StateTransition:状态机接受或拒绝的转移。
  • ExternalCall:承载由适配器记录的 HTTP、数据库、MQ 或其他外部交互结果,并与内部事件共同排序和重放。

Queue、数据库和 MQ 可以通过适配器或上层模型映射到这五类稳定事件,因此新增基础设施不会破坏核心排序、digest 与 replay 语义。

#能力与证据

能力可运行证据测试或文档证据
五类事件、稳定排序与因果关系moon run cmd/mainAPI 文档core/event_stream_test.mbt
seed 变异、digest 与失败重放moon run examples/queuecore/event_stream_test.mbtmodels/event_stream_test.mbt
消息重复投递、确认、重试与死信moon run examples/queuemodels/event_stream_test.mbt
任务依赖、取消与终态保护moon run examples/workflowmodels/event_stream_test.mbt
HTTP 记录重放兼容适配moon run examples/service_resiliencemodels/event_stream_test.mbt教程
10k smoke 与 1k/10k/100k 容量观测moon run cmd/benchmarkcore/event_stream_test.mbt

#与 MoonBit 常规测试的关系

moonsim 不替代 moon test,而是作为测试代码中的模型层运行。普通单元测试适合验证一次函数调用;moonsim 补足跨虚拟时间、跨组件、带因果关系和故障注入的行为验证。模型发现的 seed 可以写回普通测试,让同一故障持续进入 CI。

典型场景包括消息可靠性、重试与超时、任务依赖、状态机、定时器、限流熔断以及外部调用记录重放。它专注模型测试;真实网络压测和生产基础设施运行由对应工具负责。

#旗舰场景

moon run examples/queue moon run examples/workflow moon run cmd/main

examples/queue 展示生产、投递、确认超时、重试、重复投递故障、失败 seed 重放与修复策略。examples/workflow 展示依赖任务、故障注入、终态 invariant 和同 seed 重放。展示中的 FAIL (expected finding) 表示模型按预期发现业务规则被违反,命令仍正常退出。

#外部调用适配

现有 RecordedHttpTransport 和 HTTP reliability API 保持兼容。它展示了如何把请求、响应、延迟和错误记录为 ExternalCall,再与消息、任务和定时器共享确定性排序、故障证据、digest 与 replay。当前仓库提供 HTTP 记录与重放示例;数据库和 MQ 可沿用同一事件模型扩展适配器。

#包结构与稳定性

  • 根包 zlhahaha/moonsim:常用稳定 facade。
  • core/:虚拟时间、类型化事件流、seed、变异、snapshot、invariant 与 replay。
  • models/:消息、队列、任务、状态机和外部调用模型。
  • reports/:实验、seed matrix、timeline 与文本报告。

EventKind 的五种内置类型和根包入口属于 0.3.x 公共边界;具体模型字段与进阶包可继续演进。详见 API 文档教程

#本地验证

moon fmt moon check --deny-warn moon build moon info git diff --exit-code moon test --deny-warn moon run cmd/main moon run examples/queue moon run examples/workflow moon run examples/service_resilience moon run cmd/benchmark

benchmark 保留 1k、10k、100k 事件测量;不同机器的耗时不可直接比较。

  • 许可证:Apache-2.0
  • 兼容策略:0.3.x 保持根包 facade 和既有 examples 可用;API 收敛优先采用兼容新增与迁移说明。

#
Backoff

#
CircuitBreaker

#
CircuitBreakerConfig

#
CircuitBreakerResult

#
CounterSample

#
DemoDescriptor

#
Duration

Virtual simulation duration.

#
EventFailureCase

#
EventId

Stable simulation event identifier.

#
EventKind

Built-in event categories for deterministic model traces.

#
EventMutationPolicy

#
EventRecord

#
EventReplayResult

#
EventStream

#
EventStreamSnapshot

#
EventView

#
ExperimentCase

#
ExperimentReport

#
FeatureCell

#
FeatureCoverage

#
FeatureMatrix

#
ForkComparison

#
HttpFailureCase

A reproducible failing sample that can be checked after a policy change.

#
HttpOutcome

The externally observed terminal result of an HTTP exchange.

#
HttpReliabilityPolicy

Retry and reliability rules evaluated during a deterministic replay.

#
HttpReplayOptions

Optional deterministic changes applied to a recorded transport.

#
HttpReplayResult

Aggregate result and evidence from a recorded HTTP replay.

#
HttpRequest

The request metadata captured by an external HTTP integration test.

#
HttpResponse

A compact response summary. Bodies are deliberately not retained by default.

#
IntRange

#
InvariantCheck

#
InvariantReport

#
LoadBalancerConfig

#
LoadBalancerResult

#
Message

#
MessageBus

#
MetricDelta

#
MetricDiff

#
MetricSnapshot

#
Metrics

#
ModelSummary

#
NetworkConfig

#
NetworkResult

#
QueueConfig

#
QueueResult

#
QueueStats

#
RecordedHttpExchange

One HTTP exchange recorded outside the simulator.

#
RecordedHttpTransport

A pure replay transport. It never opens a socket or invokes a real service.

#
ReliabilityConfig

#
ReliabilityResult

#
ReplayBaseline

#
ReplayComparison

#
RetryConfig

#
RetryResult

#
Rng

Deterministic pseudo-random generator for reproducible simulations.

This generator is not cryptographic. It is intentionally small and portable so simulations behave the same across MoonBit targets.

#
RunReport

#
RunStep

#
RunStopReason

#
SampleDistribution

#
SampleSummary

#
Scenario

#
ScenarioCase

#
ScenarioExpectation

#
ScenarioFailure

#
ScenarioReport

#
ScenarioSuite

#
ScenarioSuiteReport

#
ScheduledEvent

A scheduled simulation event.

Events are ordered by tick, priority, then id. The id tie-breaker makes same-tick execution stable and replayable.

#
SeedMatrix

#
SeedMatrixComparison

#
ServiceResilienceConfig

#
ServiceResilienceResult

#
Sim

Deterministic simulation state.

#
SimSnapshot

#
StateMachine

#
SweepReport

#
Tick

Virtual simulation tick.

#
TimelineBucket

#
TimelineView

#
TimerPlan

#
TokenBucket

#
TokenBucketResult

#
TraceComparison

#
TraceEntry

A deterministic record of an action observed during a simulation run.

#
TraceExpectation

#
TraceKindCount

#
TraceMismatch

#
TraceQuery

#
TraceQueryResult

#
TraceStats

#
TrafficConfig

#
TrafficResult

#
Transition

#
TransitionResult

#
ValidationIssue

#
ValidationReport

#
WeightedChoice

#
WorkflowPlan

#
WorkflowResult

#
WorkflowTask

#
WorkflowTaskRun

#
build_feature_matrix

#
build_timeline

#
check_counter_at_least

fn check_counter_at_least(sim :
Sim
, name : String, expected : Int) ->
InvariantCheck

#
circuit_breaker_config

fn circuit_breaker_config(failure_threshold? : Int, reset_timeout? : Int, half_open_successes? : Int) ->
CircuitBreakerConfig

#
compare_forks

fn compare_forks(left_name : String, left :
Sim
, right_name : String, right :
Sim
) ->
ForkComparison

#
default_features

fn default_features() -> Array[String]

#
demo_descriptor

fn demo_descriptor(name : String, command : String, purpose : String, features : Array[String]) ->
DemoDescriptor

#
demo_names

fn demo_names() -> Array[String]

#
duration

fn duration(value : Int) ->
Duration

#
event_id

fn event_id(value : Int) ->
EventId

#
event_mutation_policy

fn event_mutation_policy(seed? : UInt64, max_delay? : Int, delay_percent? : Int, drop_percent? : Int, duplicate_percent? : Int, reorder_same_tick? : Bool, failure_percent? : Int) ->
EventMutationPolicy

#
event_stream

Create a generic deterministic event stream.

#
expect_trace_contains_detail

#
expect_trace_contains_kind

#
expect_trace_digest

#
expect_trace_kind_count

fn expect_trace_kind_count(entries : Array[
TraceEntry
], kind : String, expected : Int) ->
TraceExpectation

#
expect_trace_monotonic_ticks

#
expect_trace_order

fn expect_trace_order(entries : Array[
TraceEntry
], before : String, after : String) ->
TraceExpectation

#
experiment_case

#
external_call_event_kind

fn external_call_event_kind() ->
EventKind

#
find_demo

#
http_cancelled_outcome

#
http_connection_failure_outcome

fn http_connection_failure_outcome(detail : String) ->
HttpOutcome

#
http_reliability_policy

fn http_reliability_policy(seed? : UInt64, retry_limit? : Int, timeout_ticks? : Int, backoff_ticks? : Int, deadline_ticks? : Int, rate_limit_per_tick? : Int, circuit_failure_threshold? : Int, circuit_reset_ticks? : Int, accept_late_success? : Bool) ->
HttpReliabilityPolicy

#
http_replay_options

fn http_replay_options(latency_jitter? : Int, injected_failure_percent? : Int, reverse_same_tick_order? : Bool) ->
HttpReplayOptions

#
http_request

fn http_request(id : String, http_method? : String, path? : String, attempt? : Int) ->
HttpRequest

#
http_response

fn http_response(status : Int, body_summary? : String) ->
HttpResponse

#
invariant_check

fn invariant_check(name : String, passed : Bool, detail? : String) ->
InvariantCheck

#
load_balancer_config

fn load_balancer_config(seed? : UInt64, jobs? : Int, workers? : Int, max_arrival_gap? : Int, min_service? : Int, max_service? : Int, strategy? : String) ->
LoadBalancerConfig

#
load_balancer_seed_matrix

fn load_balancer_seed_matrix(seeds : Array[UInt64], strategy? : String) ->
SeedMatrix

#
message_event_kind

#
metrics_counter_delta

fn metrics_counter_delta(left :
Metrics
, right :
Metrics
, name : String) -> Int

#
network_config

fn network_config(seed? : UInt64, messages? : Int, latency_min? : Int, latency_max? : Int, drop_percent? : Int, retry_delay? : Int) ->
NetworkConfig

#
network_seed_matrix

fn network_seed_matrix(seeds : Array[UInt64]) ->
SeedMatrix

#
queue_config

fn queue_config(seed? : UInt64, customers? : Int, max_arrival_gap? : Int, service_time? : Int) ->
QueueConfig

#
recorded_http_exchange

#
reliability_config

fn reliability_config(seed? : UInt64, operations? : Int, fail_percent? : Int, retry_limit? : Int, backoff? : Int) ->
ReliabilityConfig

#
render_demo_catalog

fn render_demo_catalog() -> String

#
render_experiment_report

fn render_experiment_report(report :
ExperimentReport
) -> String

#
render_feature_coverage

fn render_feature_coverage() -> String

#
render_feature_matrix

fn render_feature_matrix() -> String

#
render_metric_diff

fn render_metric_diff(diff :
MetricDiff
) -> String

#
render_metrics_report

fn render_metrics_report(metrics :
Metrics
) -> String

#
render_model_summaries

fn render_model_summaries(summaries : Array[
ModelSummary
]) -> String

#
render_replay_comparison

fn render_replay_comparison(comparison :
ReplayComparison
) -> String

#
render_sample_distribution

fn render_sample_distribution(metrics :
Metrics
, name : String) -> String

#
render_scenario_report

fn render_scenario_report(report :
ScenarioReport
) -> String

#
render_scenario_suite_report

fn render_scenario_suite_report(report :
ScenarioSuiteReport
) -> String

#
render_service_resilience_report

fn render_service_resilience_report(result :
ServiceResilienceResult
) -> String

#
render_sim_report

fn render_sim_report(sim :
Sim
, title? : String) -> String

#
render_trace_report

fn render_trace_report(entries : Array[
TraceEntry
], limit? : Int) -> String

#
replay_baseline

#
retry_config

fn retry_config(seed? : UInt64, max_attempts? : Int, fail_until? : Int, initial_backoff? : Int, jitter? : Int) ->
RetryConfig

#
retry_seed_matrix

fn retry_seed_matrix(seeds : Array[UInt64]) ->
SeedMatrix

#
retry_timeout_fault_policy

#
retry_timeout_fixed_policy

#
retry_timeout_recording

#
run_circuit_breaker_model

fn run_circuit_breaker_model(seed? : UInt64, calls? : Int) ->
CircuitBreakerResult

#
run_token_bucket_model

fn run_token_bucket_model(seed? : UInt64, requests? : Int) ->
TokenBucketResult

#
service_resilience_config

fn service_resilience_config(seed? : UInt64, requests? : Int, workers? : Int, queue_limit? : Int, timeout_ticks? : Int, retry_limit? : Int, base_latency? : Int, jitter? : Int, fail_percent? : Int, drop_percent? : Int, rate_limit_capacity? : Int, rate_limit_refill? : Int, rate_limit_interval? : Int, min_success_percent? : Int) ->
ServiceResilienceConfig

#
service_resilience_seed_matrix

fn service_resilience_seed_matrix(seeds : Array[UInt64]) ->
SeedMatrix

#
state_transition_event_kind

fn state_transition_event_kind() ->
EventKind

#
sweep_load_balancer_strategies

fn sweep_load_balancer_strategies(seed? : UInt64, jobs? : Int) ->
SweepReport

#
sweep_point

#
sweep_reliability_failure_rates

fn sweep_reliability_failure_rates(seed? : UInt64) ->
SweepReport

#
task_event_kind

#
tick

fn tick(value : Int) ->
Tick

#
timer_event_kind

#
timer_plan

fn timer_plan(name : String, start_after? : Int, interval? : Int, times? : Int, priority? : Int) ->
TimerPlan

#
trace_digest

fn trace_digest(entries : Array[
TraceEntry
]) -> UInt64

#
trace_entry

fn trace_entry(tick : Int, event_id : Int, kind : String, detail : String) ->
TraceEntry

#
trace_filter_detail

#
trace_filter_kind

#
trace_filter_tick_range

fn trace_filter_tick_range(entries : Array[
TraceEntry
], min_tick : Int, max_tick : Int) -> Array[
TraceEntry
]

#
trace_query

fn trace_query(kind? : String, detail? : String, min_tick? : Int, max_tick? : Int) ->
TraceQuery

#
trace_to_text

fn trace_to_text(entries : Array[
TraceEntry
]) -> String

#
traffic_config

fn traffic_config(seed? : UInt64, cycles? : Int, cars? : Int, cycle_ticks? : Int) ->
TrafficConfig

#
transition

fn transition(from : String, event : String, to : String, action? : String) ->
Transition

#
validation_issue

fn validation_issue(code : String, message : String, severity? : String) ->
ValidationIssue

#
version

fn version() -> String

#
weighted_choice

fn weighted_choice(label : String, weight : Int) ->
WeightedChoice

#
workflow_critical_path

fn workflow_critical_path(plan :
WorkflowPlan
) -> Int

Source Files