Salta ai contenuti

Chaos: harness deterministico per scenari di resilienza

Il modulo Chaos è un harness leggero per i test di resilienza. Consente di registrare scenari di iniezione di guasti che implementano un’interfaccia a un solo metodo, eseguirli e raccogliere un report pass/fail strutturato. È volutamente minimale — cinque classi — ed è pensato per suite di resilienza ed esercizi di chaos-day, non per il flusso di produzione dei documenti.

Stabilità: sperimentale. Si tratta di una superficie per strumenti di test e resilienza, non un’API PDF di base. L’SPI è piccolo e stabile nella forma, ma l’ambito del modulo e gli scenari inclusi evolvono. Non basarvi sopra flussi di controllo di produzione.

Terminal window
composer require nextpdf/core:^3

Un test di resilienza si chiede: quando una dipendenza fallisce, il motore degrada correttamente? Il modulo Chaos fornisce a quel test una struttura. ChaosScenarioInterface è il contratto che uno scenario implementa — un name() e un simulate() che restituisce un ChaosOutcome. Uno scenario incapsula un singolo guasto (una partizione di rete, una raffica di risposte 5xx dal backend di recupero) e riporta ciò che è accaduto.

ChaosScenarioRunner è l’orchestratore. Si registrano gli scenari con register(), si chiama run() per eseguirli in sequenza nell’ordine di registrazione e si consultano i dati aggregati: outcomes(), allPassed(), passCount(), failCount(). Il runner non solleva mai un’eccezione per il fallimento di uno scenario — un fallimento è un dato registrato in un ChaosOutcome, non un’eccezione. Solleva un’eccezione solo quando è compromessa la propria infrastruttura: una registrazione di scenario non valida o l’impossibilità di scrivere il file del report (ChaosReportWriteException). Uno scenario che non riesce a raggiungere la risorsa che sta testando fa emergere una RetrievalUnavailableException. L’intero modulo è @since 3.2.0.

ChaosOutcome è il risultato del singolo scenario: pass/fail, una durata e un toArray() per il report strutturato. Poiché un esito registra la durata in tempo reale, il profilo di riproducibilità del report è structural, non bitwise.

TipoMembri principaliRuolo
ChaosScenarioInterfacename(): string, simulate(): ChaosOutcomeContratto dello scenario (@since 3.2.0)
ChaosScenarioRunnerregister(), run(), outcomes(), allPassed(), passCount(), failCount()Orchestratore sequenziale di scenari (@since 3.2.0)
ChaosOutcomedurationSeconds(), toArray()Risultato pass/fail del singolo scenario (@since 3.2.0)
RetrievalUnavailableExceptionLa risorsa testata era irraggiungibile
ChaosReportWriteExceptionNon è stato possibile scrivere il file del report

Eseguire composer docs:generate-api-php -- --module=Chaos per ottenere la tabella PHPDoc completa.

Registrare uno scenario ed eseguire la suite.

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

Eseguire l’harness da un job di resilienza e trattare qualsiasi fallimento come un codice di uscita diverso da zero, senza lasciare che il fallimento di uno scenario emerga come eccezione.

<?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() non solleva mai un’eccezione per il fallimento di uno scenario. Un fallimento è contenuto in ChaosOutcome. Se si racchiude run() in un try/catch aspettandosi di intercettare lì i fallimenti, non li si vedrà mai — leggere invece failCount() / allPassed().
  • Il runner solleva un’eccezione solo per guasti dell’infrastruttura: una registrazione errata o una ChaosReportWriteException quando il percorso del report non è scrivibile. Gestire questi casi separatamente dai risultati degli scenari.
  • Gli scenari vengono eseguiti in sequenza nell’ordine di registrazione. Non c’è parallelismo. L’ordine può essere rilevante se gli scenari condividono uno stato esterno.
  • Questo modulo è destinato ai test di resilienza. Non importare il runner nel flusso di produzione dei documenti come meccanismo di controllo.

L’overhead del runner è trascurabile. Il costo dipende da ciò che fanno gli scenari. Poiché gli scenari iniettano guasti e possono attendere timeout, un’esecuzione di chaos può essere lenta per progettazione. Il performance_budget qui è il valore di riferimento del motore, non un limite alla durata degli scenari. Il profilo di riproducibilità è structural: il report registra le durate in tempo reale, quindi due esecuzioni differiscono in quei campi.

Gli scenari iniettano guasti e possono sollecitare i percorsi di fallimento nelle dipendenze. Eseguire l’harness solo in un ambiente di test o di staging con credenziali ed endpoint circoscritti a quell’ambiente — mai contro sistemi di produzione. Il report può contenere dettagli diagnostici sulle modalità di guasto. Trattarlo come interno e applicare la sanitizzazione dei log prevista dal progetto prima di condividerlo. Consultare il modello di minaccia del motore in /modules/core/security/.

Questo modulo non formula alcuna affermazione normativa sulla specifica PDF. È uno strumento di resilienza. Non implementa alcun protocollo standardizzato le cui clausole debbano essere citate. La conformità del motore è convalidata dall’oracolo e dalle suite golden descritte in /modules/core/conformance/.