コンテンツにスキップ

Chaos: 決定論的なレジリエンスシナリオのためのハーネス

Chaos モジュールは、レジリエンステスト向けの小規模なハーネスです。1 メソッドのインターフェイスを実装する障害注入シナリオを登録し、それらを実行して、構造化された pass/fail レポートを収集します。意図的に最小限に抑えられており(5 つのクラス)、レジリエンススイートやカオスデイの演習を想定したもので、ドキュメント生成パス向けではありません。

安定性: 実験的(experimental)。 これはテストおよびレジリエンス用のツール面であり、 コアの PDF API ではありません。SPI は小規模で、形状としては安定していますが、モジュールのスコープと同梱シナリオは今後も進化します。本番の制御フローをこれに依存して構築しないでください。

Terminal window
composer require nextpdf/core:^3

レジリエンステストで問うのは、依存先が失敗したときにエンジンが適切に劣化できるかという点です。Chaos モジュールは、そのテストに構造を与えます。ChaosScenarioInterface は、シナリオが実装するコントラクトです。name() と、ChaosOutcome を返す simulate() から構成されます。シナリオは 1 つの障害(ネットワーク分断、取得バックエンドからの 5xx 応答の連続など)をカプセル化し、何が起きたかを報告します。

ChaosScenarioRunner はオーケストレーターです。シナリオを register() で登録し、run() を呼び出して登録順に逐次実行し、集約結果として outcomes()allPassed()passCount()failCount() を読み取ります。ランナーは、シナリオの 失敗 を理由に例外をスローすることは決してありません。失敗は例外ではなく、ChaosOutcome に記録されるデータです。スローするのは、ランナー自身のインフラが壊れている場合のみです。すなわち、不正なシナリオ登録、またはレポートファイルを書き込めない場合(ChaosReportWriteException)です。シナリオでテスト対象のリソースに到達できない場合は、RetrievalUnavailableException が表面化します。モジュール全体に @since 3.2.0 が適用されます。

ChaosOutcome は、シナリオごとの結果を表します。内容は pass/fail、所要時間、および構造化レポート向けの toArray() です。結果はウォールクロックの所要時間を記録するため、レポートの再現性プロファイルは bitwise ではなく structural です。

主なメンバー役割
ChaosScenarioInterfacename(): string, simulate(): 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レポートファイルを書き込めなかった場合

完全な PHPDoc テーブルを生成するには、composer docs:generate-api-php -- --module=Chaos を実行してください。

シナリオを登録し、スイートを実行します。

<?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";

レジリエンスジョブからハーネスを駆動し、シナリオの失敗を例外として外部に漏らさず、失敗があれば非ゼロの終了コードとして扱います。

<?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() を読み取ってください。
  • ランナーが例外をスローするのはインフラ障害の場合のみです。すなわち、不正な登録、またはレポートパスが書き込み不可のときの ChaosReportWriteException です。これらはシナリオ結果とは区別して処理してください。
  • シナリオは登録順に逐次実行されます。並列実行はありません。シナリオが外部状態を共有する場合は、順序の影響を受けることがあります。
  • このモジュールはレジリエンステスト用です。ランナーを制御メカニズムとしてドキュメント生成パスに取り込まないでください。

ランナーのオーバーヘッドは無視できる程度です。コストは、シナリオの処理内容によって決まります。シナリオは障害を注入し、タイムアウトを待つことがあるため、カオス実行は設計上、時間がかかることがあります。ここでの performance_budget は、エンジン向けの参考値であり、シナリオの所要時間を制限するものではありません。再現性プロファイルは structural です。レポートはウォールクロックの所要時間を記録するため、2 回の実行では該当フィールドが異なります。

シナリオは障害を注入し、依存先の失敗パスを通ることがあります。ハーネスの実行は、対象環境にスコープを限定した資格情報とエンドポイントを備えるテスト環境またはステージング環境のみに留めてください。本番システムに対しては決して実行しないでください。レポートには、障害モードに関する診断情報が含まれることがあります。これを内部情報として扱い、共有する前にプロジェクトのログスクラブ義務を適用してください。エンジンの脅威モデルについては /modules/core/security/ を参照してください。

このモジュールは、PDF 仕様について規範的な主張を一切行いません。これはレジリエンス用のツールです。条項引用を必要とする標準化プロトコルは実装していません。エンジンの適合性は、/modules/core/conformance/ に記載のオラクルおよびゴールデンスイートによって検証されます。