跳转到内容

Chaos:确定性韧性场景测试框架

Chaos 模块是用于韧性测试的小型框架。你可以注册实现单方法接口的故障注入场景、执行这些场景,并收集一份结构化 pass/fail 报告。它刻意保持极简——仅有五个类——用于韧性测试套件与混沌演练(chaos-day),而不是文档生成路径。

稳定性:实验性。 这是一个测试与韧性工具层, 并非核心 PDF API。其 SPI 规模小且形态稳定,但本模块的范围与随附场景仍在演进。请勿基于它构建生产环境控制流程。

Terminal window
composer require nextpdf/core:^3

韧性测试关注的是:当某个依赖项失效时,引擎是否能正确降级?Chaos 模块为这类测试提供结构。ChaosScenarioInterface 是场景需要实现的契约——包含一个 name(),以及一个返回 ChaosOutcomesimulate()。一个场景封装单个故障(网络分区、一系列检索后端的 5xx 响应),并报告发生的情况。

ChaosScenarioRunner 是协调者。你会用 register() 注册场景、调用 run() 按注册顺序依次执行它们,并读取汇总结果:outcomes()allPassed()passCount()failCount()。此 runner 绝不会因场景失败而抛出异常——失败是捕获到 ChaosOutcome 中的数据,而不是异常。它只会在自身基础设施故障时抛出异常:无效的场景注册,或无法写入报告文件(ChaosReportWriteException)。无法访问受测资源的场景,会暴露一个 RetrievalUnavailableException。整个模块标记为 @since 3.2.0

ChaosOutcome 是每个场景的结果:pass/fail、一段持续时间,以及供结构化报告使用的 toArray()。由于 outcome 会记录墙钟(wall-clock)持续时间,该报告的可复现性配置为 structural,而非 bitwise

类型主要成员角色
ChaosScenarioInterfacename(): stringsimulate(): ChaosOutcome场景契约(@since 3.2.0
ChaosScenarioRunnerregister()run()outcomes()allPassed()passCount()failCount()顺序场景协调者(@since 3.2.0
ChaosOutcomedurationSeconds()toArray()每个场景的 pass/fail 结果(@since 3.2.0
RetrievalUnavailableException无法访问某个受测资源
ChaosReportWriteException无法写入报告文件

执行 composer docs:generate-api-php -- --module=Chaos 获取完整的 PHPDoc 表格。

注册一个场景并执行测试套件。

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Chaos\ChaosOutcome;
use NextPDF\Chaos\ChaosScenarioInterface;
use NextPDF\Chaos\ChaosScenarioRunner;
final class TimeoutScenario implements ChaosScenarioInterface
{
public function name(): string
{
return 'dependency-timeout';
}
public function simulate(): ChaosOutcome
{
// Inject the fault, observe the engine's degradation, return the verdict.
return new ChaosOutcome(/* name, passed, durationSeconds, details */);
}
}
$runner = new ChaosScenarioRunner();
$runner->register(new TimeoutScenario());
$runner->run();
echo $runner->allPassed() ? "Resilient.\n" : "{$runner->failCount()} scenario(s) failed.\n";

在韧性作业(resilience job)中驱动此框架,将任何失败视为非零退出码,同时避免让场景失败以异常形式逸出。

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Chaos\ChaosScenarioRunner;
use NextPDF\Chaos\Exception\ChaosReportWriteException;
use Psr\Log\LoggerInterface;
final readonly class ChaosJob
{
/** @param list<\NextPDF\Chaos\ChaosScenarioInterface> $scenarios */
public function __construct(
private array $scenarios,
private LoggerInterface $logger,
) {}
public function run(string $reportPath): int
{
$runner = new ChaosScenarioRunner();
foreach ($this->scenarios as $scenario) {
$runner->register($scenario);
}
$runner->run(); // never throws on scenario failure
try {
$runner->writeReport($reportPath);
} catch (ChaosReportWriteException $e) {
$this->logger->error('Chaos report could not be written.', ['error' => $e->getMessage()]);
return 2;
}
return $runner->allPassed() ? 0 : 1;
}
}
  • run() 绝不会因为某个场景失败而抛出异常。失败存在于 ChaosOutcome 之中。如果你把 run() 包在一个预期能在其中捕获失败的 try/catch 中,你永远不会看到那些失败——请改为读取 failCount() / allPassed()
  • 此 runner 只会在基础设施故障时抛出异常:注册错误,或报告路径不可写时的 ChaosReportWriteException。请将这些异常与场景结果分开处理。
  • 场景会按注册顺序依次执行。没有并行处理。若多个场景共用外部状态,执行顺序可能会影响结果。
  • 本模块用于韧性测试。请勿将此 runner 作为控制机制接入文档生成路径。

此 runner 的开销可以忽略不计。成本取决于场景实际执行的工作。由于场景会注入故障并可能等待超时,一次混沌执行按设计本来就可能很慢。此处的 performance_budget 是引擎参考值,并非场景持续时间的上限。其可复现性配置为 structural:报告会记录墙钟持续时间,因此两次执行在这些字段上会有所不同。

场景会注入故障,并可能触发依赖项中的失败路径。只在测试或预发布(staging)环境中执行此框架,并确保其凭据与端点都限定在该环境内——绝不可针对生产环境系统执行。报告可能包含关于失败模式的诊断细节。请将其视为内部数据,并在分享前按本项目的日志清理(log-scrubbing)要求处理。请参阅 /modules/core/security/ 中的引擎威胁模型。

本模块不提出任何关于 PDF 规范的规范性声明。它是韧性工具。它未实现任何需要引用条款的标准化协议。引擎符合性由 /modules/core/conformance/ 中所述的 oracle 与黄金套件(golden suites)验证。