Zum Inhalt springen

Chaos: deterministisches Harness für Resilienzszenarien

Das Chaos-Modul ist ein kleines Harness für Resilienztests. Sie registrieren Fault-Injection-Szenarien, die eine einmethodige Schnittstelle implementieren, führen sie aus und erhalten einen strukturierten pass/fail-Bericht. Es ist bewusst minimal gehalten — fünf Klassen — und für Resilienzsuiten und Chaos-Day-Übungen gedacht, nicht für den Pfad der Dokumenterzeugung.

Stabilität: experimentell. Dies ist eine Test- und Resilienz-Tooling-Fläche, keine zentrale PDF-API. Das SPI ist klein und in seiner Form stabil, doch Umfang des Moduls und die mitgelieferten Szenarien entwickeln sich weiter. Stützen Sie keinen produktiven Steuerungsfluss darauf auf.

Terminal-Fenster
composer require nextpdf/core:^3

Ein Resilienztest stellt die Frage: Verhält sich die Engine beim Ausfall einer Abhängigkeit kontrolliert und abgestuft? Das Chaos-Modul gibt diesem Test eine Struktur. ChaosScenarioInterface ist der Vertrag, den ein Szenario implementiert — mit name() und simulate(), das ein ChaosOutcome zurückgibt. Ein Szenario kapselt einen Fehlerfall (eine Netzwerkpartitionierung, eine Häufung von 5xx-Antworten des Retrieval-Backends) und meldet, was passiert ist.

ChaosScenarioRunner ist der Orchestrator. Sie registrieren Szenarien mit register(), rufen run() auf, um sie der Reihe nach in Registrierungsreihenfolge auszuführen, und lesen die aggregierten Ergebnisse aus: outcomes(), allPassed(), passCount(), failCount(). Der Runner wirft bei einem Szenario-Fehlschlag niemals eine Ausnahme — ein Fehlschlag ist ein Datenpunkt, der in einem ChaosOutcome festgehalten wird, keine Ausnahme. Er wirft nur, wenn seine eigene Infrastruktur defekt ist: eine ungültige Szenario-Registrierung oder wenn die Berichtsdatei nicht geschrieben werden kann (ChaosReportWriteException). Ein Szenario, das die getestete Ressource nicht erreichen kann, meldet eine RetrievalUnavailableException. Das gesamte Modul ist @since 3.2.0.

ChaosOutcome ist das Ergebnis pro Szenario: pass/fail, eine Dauer und ein toArray() für den strukturierten Bericht. Da ein Outcome die Wall-Clock-Dauer aufzeichnet, ist das Reproduzierbarkeitsprofil des Berichts structural, nicht bitwise.

TypWichtige MitgliederRolle
ChaosScenarioInterfacename(): string, simulate(): ChaosOutcomeDer Vertrag für Szenarien (@since 3.2.0)
ChaosScenarioRunnerregister(), run(), outcomes(), allPassed(), passCount(), failCount()Sequenzieller Szenario-Orchestrator (@since 3.2.0)
ChaosOutcomedurationSeconds(), toArray()Ergebnis pro Szenario (pass/fail) (@since 3.2.0)
RetrievalUnavailableExceptionEine getestete Ressource war nicht erreichbar
ChaosReportWriteExceptionDie Berichtsdatei konnte nicht geschrieben werden

Führen Sie composer docs:generate-api-php -- --module=Chaos aus, um die vollständige PHPDoc-Tabelle auszugeben.

Registrieren Sie ein Szenario und führen Sie die Suite aus.

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

Rufen Sie das Harness aus einem Resilienz-Job heraus auf und behandeln Sie jeden Fehlschlag als Exit-Code ungleich null, ohne Szenario-Fehlschläge als Ausnahmen nach außen dringen zu lassen.

<?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() wirft niemals eine Ausnahme, nur weil ein Szenario fehlgeschlagen ist. Ein Fehlschlag wird in ChaosOutcome festgehalten. Wenn Sie run() mit try/catch umschließen und dort Fehlschläge erwarten, werden diese dort niemals auftauchen — lesen Sie stattdessen failCount() / allPassed().
  • Der Runner wirft nur bei Infrastrukturfehlern: bei einer fehlerhaften Registrierung oder bei einer ChaosReportWriteException, wenn der Berichtspfad nicht beschreibbar ist. Behandeln Sie diese getrennt von den Szenario-Ergebnissen.
  • Szenarien laufen der Reihe nach in Registrierungsreihenfolge. Es gibt keine Parallelität. Die Reihenfolge kann eine Rolle spielen, wenn Szenarien externen Zustand gemeinsam nutzen.
  • Dieses Modul dient Resilienztests. Importieren Sie den Runner nicht als Steuerungsmechanismus in den Pfad der Dokumenterzeugung.

Der Overhead des Runners ist vernachlässigbar. Die Kosten ergeben sich aus den Aktionen der Szenarien. Da Szenarien Fehler injizieren und auf Timeouts warten können, kann ein Chaos-Lauf konstruktionsbedingt langsam sein. Das performance_budget ist hier der Referenzwert der Engine, keine Obergrenze für die Szenario-Dauer. Das Reproduzierbarkeitsprofil ist structural: Der Bericht zeichnet Wall-Clock-Dauern auf, sodass sich zwei Läufe in diesen Feldern unterscheiden.

Szenarien injizieren Fehler und können Fehlerpfade in Abhängigkeiten auslösen. Führen Sie das Harness nur in einer Test- oder Staging-Umgebung mit auf diese Umgebung beschränkten Anmeldedaten und Endpunkten aus — niemals gegen Produktivsysteme. Der Bericht kann diagnostische Details zu Fehlermodi enthalten. Behandeln Sie ihn als intern und wenden Sie die Log-Scrubbing-Pflicht des Projekts an, bevor Sie ihn weitergeben. Siehe das Bedrohungsmodell der Engine unter /modules/core/security/.

Dieses Modul erhebt keinen normativen Anspruch in Bezug auf die PDF-Spezifikation. Es ist Resilienz-Tooling. Es implementiert kein standardisiertes Protokoll, dessen Klauseln zitiert werden müssten. Die Konformität der Engine wird durch die Oracle- und Golden-Suiten validiert, die unter /modules/core/conformance/ beschrieben sind.