Enterprise Edition
Stream: Verarbeitung von Dokumentaufträgen
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“NextPDF\Enterprise\Stream\DocumentJobStreamProcessor verwandelt einen Strom von Render-Manifesten in dauerhafte, nachvollziehbare Ergebnisse. Er konsumiert ein iterable<RenderManifest> als Generator, rendert begrenzte Fenster über die Pro-Render-Engine und finalisiert jeden Auftrag in Quellreihenfolge. Jeder Auftrag endet in genau einem Endzustand: Ausgabe übergeben, als bereits übergeben erkannt oder dead-lettered. Der Fortschritt wird per Checkpoint gesichert, sodass ein abgestürzter Lauf ohne erneute Veröffentlichung wiederaufgenommen wird.
Die Stream-Geschichte teilt sich über zwei Editionen auf, und die Aufteilung ist beabsichtigt. Pro stellt die dauerhafte, nebenläufige Render-Engine und die lokalen Single-Host-Dateisystemspeicher bereit — die In-Process-Hälfte. Enterprise stellt diesen Dokumentauftrags-Stream-Prozessor bereit sowie die Bausteine, die Hostgrenzen überschreiten: den Objektspeicher-Committer (ObjectStorageCommitter) und die dauerhafte Outbox terminaler Ereignisse (FilesystemOutboxEmitter). Die Pro-Stream-Seite beschreibt dieselbe Grenze von ihrer Seite aus.
Verfügbarkeit & Lizenzierung
Abschnitt betitelt „Verfügbarkeit & Lizenzierung“Diese Fähigkeit wird in NextPDF Enterprise (nextpdf/enterprise) ausgeliefert und aktiviert sich mit einer Lizenzhülle der Enterprise-Stufe. Eine Bereitstellung ohne diese Berechtigung lädt die Klassen der Fähigkeit nicht. Editionen vergleichen und eine Lizenz erwerben.
Installation
Abschnitt betitelt „Installation“composer require nextpdf/enterpriseDie Klassen auf dieser Seite liegen unter NextPDF\Enterprise\Stream und NextPDF\Enterprise\Stream\Storage. Sie konsumieren die eingefrorenen Pro-Verträge in NextPDF\Pro\Stream — Engine-, Committer-, Checkpoint-, Idempotenz-, Retry- und Dead-Letter-Schnittstellen.
Konzeptioneller Überblick
Abschnitt betitelt „Konzeptioneller Überblick“Die Aufgabe des Prozessors ist die Auslieferungssemantik, nicht das Rendern. Er gruppiert den Manifest-Stream in Quell-Offset-Fenster, die nicht größer als die Batch-Größe der Engine sind. Jedes Fenster rendert über RenderEngineInterface::renderBatch(), mit begrenzter, deterministischer Wiederholung von Timeouts pro Element. Danach wird jedes Element in Quell-Offset-Reihenfolge zu einem Endzustand finalisiert.
Die Exactly-once-Grenze verläuft pro Element und ist im Committer verankert, nicht in der Koordination. Der dauerhafte Fortschritt ist ein 1-basierter Offset-High-Watermark: Jeder Offset an oder unterhalb des Checkpoints hat einen Endzustand erreicht. Die Barriere-Reihenfolge ist festgelegt: die Bytes übergeben, den Watermark vorrücken, den Checkpoint speichern, gepufferte Idempotenzmarken leeren, dann terminale Ereignisse emittieren. Ein Absturz zwischen Übergabe und Checkpoint übergibt bei der Wiederaufnahme idempotent erneut, weil der Committer Digests vergleicht. Ein Absturz nach dem Checkpoint springt am Offset vorbei, sodass nichts zweimal veröffentlicht wird.
ObjectStorageCommitter implementiert das Pro-OutputCommitterInterface gegen einen Objektspeicher über das minimale ObjectStorageClientInterface. Der container des Ziels ist der Bucket und dessen key der Objektschlüssel. Das erneute Übergeben identischer Bytes ist ein digest-verglichener No-op. Abweichende Bytes ohne overwrite lösen den SPEC-COMMIT-409-Konflikt aus. Ein frisches Objekt wird nur mit dem atomaren Conditional-Write putIfAbsent() angelegt; wird dieses Rennen verloren, löst das eine begrenzte Re-Read-and-Resolve-Schleife aus. Cross-Writer-Exactly-once gilt daher genau so weit, wie das putIfAbsent() Ihres Adapters ein echter Conditional-Write ist — If-None-Match: * auf S3, ifGenerationMatch: 0 auf GCS. Dieser Zyklus liefert die Schnittstelle plus den In-Memory-NullObjectStorageClient; der Live-S3/GCS-Adapter wird vom Host bereitgestellt.
Terminale Ereignisse schließen den Kreis für nachgelagerte Systeme. Nach der Checkpoint-Barriere versucht der Prozessor, für jeden finalisierten Auftrag ein JobTerminalEvent zu emittieren — Bezeichner, Status, Quittung, Fehlerdetails, Versuchsanzahl und niemals irgendwelche PDF-Bytes. Mit einem einfachen Callback-Emitter ist die Emission at-most-once: Ereignisse nach einem Checkpoint können bei einer Absturz-Wiederaufnahme übersprungen werden. FilesystemOutboxEmitter macht jedes Ereignis dauerhaft, sobald emit() läuft: Jedes Ereignis ist eine atomare JSON-Datei, benannt nach einem Hash ihrer deterministischen eventId, sodass ein erneutes Emittieren nach einer Wiederaufnahme idempotent ist, ein Relay at-least-once ausliefert und Konsumenten anhand der eventId deduplizieren. Eine Grenze bleibt in beiden Fällen bestehen: Die Emission erfolgt nach der Checkpoint-Barriere, sodass ein Absturz zwischen checkpoint.save() und emit() das terminale Ereignis dieses Elements bei der Wiederaufnahme überspringt. Nachgelagerte Systeme, die ein vollständiges Ereignis-Ledger benötigen, sollten gegen die übergebenen Objekte abgleichen (der Speicher ist die Quelle der Wahrheit), nicht allein gegen die Outbox.
Die Lizenzierung ist in den Ausgabepfad eingebunden. Die Factory withBrandingFromLicense() löst einmal pro Lauf aus der Lizenz eine Evaluations-Branding-Strategie auf. Eine bezahlte Lizenz löst sich in eine Identitätstransformation auf. Eine Evaluations- oder fehlende Lizenz versieht jedes übergebene Dokument mit einem Wasserzeichen, und ein Dokument, das nicht gebrandet werden kann, wird dead-lettered — der Prozessor übergibt niemals ungebrandete Evaluations-Bytes.
Warum es so funktioniert
Abschnitt betitelt „Warum es so funktioniert“Die tragende Entscheidung ist, dass Exactly-once auf dem digest-verglichenen, bedingt angelegten Objekt des Committers ruht — nicht auf verteilten Sperren oder Konsens. Der Conditional-Write des Objektspeichers ist das einzige atomare Primitiv, das das Design benötigt, und alles andere darf ausfallen und sich erholen. Deshalb muss die Render-Engine nebenwirkungsfrei bleiben, deshalb werden Keyed-State- und In-Run-Dedup-Caches als neuberechenbare Beschleunigungen behandelt, und deshalb bricht eine mehrdeutige Übergabe den Lauf ab, anstatt zu raten: Der Wiederaufnahmepfad konvergiert über denselben Digest-Vergleich. Deshalb ist auch Single-Writer pro runId eine erklärte Anforderung und keine erzwungene Lease — der Checkpoint-Speicher bleibt bewusst einfach, und die Commit-Schicht bleibt das Sicherheitsnetz.
Design-Hintergrund: Dokumenterzeugung in hohem Volumen.
API-Oberfläche
Abschnitt betitelt „API-Oberfläche“DocumentJobStreamProcessor
Abschnitt betitelt „DocumentJobStreamProcessor“Hosts sollten über die Factory konstruieren, damit die Lizenz-zu-Branding-Steuerung nie unverdrahtet bleibt:
public static function withBrandingFromLicense( RenderEngineInterface $engine, OutputCommitterInterface $committer, IdempotencyStoreInterface $idempotency, CheckpointStoreInterface $checkpoints, KeyedStateStoreInterface $state, DeadLetterStoreInterface $deadLetters, RetryPolicy $retryPolicy, ClockInterface $clock, EntitlementEvaluator $entitlementEvaluator, ?LicenseKey $license, ?StreamProcessorProbe $probe = null, ?JobCompletionEmitterInterface $emitter = null, ?BrandingApplicator $brandingApplicator = null,): self$clock ist Symfony\Component\Clock\ClockInterface (der Retry-Backoff schläft über ihn). Eine null-Lizenz löst sich fail-closed in Evaluations-Branding auf.
Der einzige Einstiegspunkt verarbeitet einen Lauf und gibt dessen Zähler zurück:
public function process(iterable $manifests, StreamProcessorConfig $config): ProcessingSummaryWirft oder scheitert mit: NextPDF\Enterprise\Stream\Exception\StreamProcessorException, wenn Absturzsicherheits-Vorbedingungen scheitern (ein crashSafe-Lauf mit nicht-dauerhaften Kollaboratoren) oder wenn eine Übergabe mehrdeutig ist; InvalidArgumentException, wenn windowSize das maxBatchSize() der Engine überschreitet.
StreamProcessorConfig
Abschnitt betitelt „StreamProcessorConfig“public function __construct( public string $runId, int $windowSize = 32, int $checkpointIntervalJobs = 100, public bool $crashSafe = true, public bool $emitSkippedCompletions = false,)Wirft oder scheitert mit: InvalidArgumentException, wenn windowSize oder checkpointIntervalJobs unter 1 liegt. $runId ist der stabile Single-Writer-Lauf-Bezeichner, der die Checkpoint-Wiederaufnahme verschlüsselt.
ObjectStorageCommitter
Abschnitt betitelt „ObjectStorageCommitter“public function __construct( private ObjectStorageClientInterface $client, private string $scheme, private ClockInterface $clock,) {}$scheme benennt das Zielschema, das dieser Committer bedient (zum Beispiel s3 oder gcs); $clock ist hier Psr\Clock\ClockInterface.
public function commit( string $jobId, OutputObjectKey $target, string $bytes, string $sha256, bool $overwrite = false,): CommitReceiptWirft oder scheitert mit: UnsupportedTargetException bei einem Schema-Mismatch; RenderManifestException, wenn der Zielschlüssel nicht container-relativ-sicher ist; CommitIntegrityException, wenn der deklarierte sha-256 nicht mit den Bytes übereinstimmt; OutputCommitConflictException (SPEC-COMMIT-409) bei abweichenden Bytes ohne overwrite; RuntimeException, wenn das Anlege-Rennen nach 5 Versuchen unter nebenläufiger Mutation nicht konvergieren kann.
ObjectStorageClientInterface
Abschnitt betitelt „ObjectStorageClientInterface“Die minimale Adapter-Oberfläche, die eine Live-S3/GCS-Integration implementiert:
public function shaOf(string $bucket, string $key): ?string;
public function put(string $bucket, string $key, string $bytes, string $sha256): void;
public function putIfAbsent(string $bucket, string $key, string $bytes, string $sha256): bool;putIfAbsent() muss ein echtes atomares bedingtes Anlegen sein (If-None-Match: * auf S3, ifGenerationMatch: 0 auf GCS) und gibt nur dann true zurück, wenn dieser Aufruf das Objekt geschrieben hat. put() ist das bedingungslose Überschreiben, das ausschließlich verwendet wird, wenn das Manifest overwrite angefordert hat.
JobCompletionEmitterInterface und FilesystemOutboxEmitter
Abschnitt betitelt „JobCompletionEmitterInterface und FilesystemOutboxEmitter“public function emit(JobTerminalEvent $event): void;Der Emitter ist am Prozessor optional. Ereignisse feuern erst, nachdem ein Element dauerhaft finalisiert wurde. FilesystemOutboxEmitter ist die ausgelieferte dauerhafte Implementierung:
public function __construct(string $directory, ?AtomicFileWriter $writer = null)Wirft oder scheitert mit: InvalidArgumentException, wenn das Verzeichnis nicht existiert; emit() wirft RuntimeException, wenn ein Ereignis nicht JSON-kodiert werden kann. hasEvent(string $eventId): bool prüft die Outbox; count(): int meldet nicht ausgelieferte Ereignisse.
JobTerminalEvent und JobTerminalStatus
Abschnitt betitelt „JobTerminalEvent und JobTerminalStatus“public function __construct( public string $eventId, public string $runId, public int $sourceOffset, public string $jobId, public string $idempotencyKeyValue, public JobTerminalStatus $status, public ?CommitReceipt $receipt, public ?string $errorCode, public ?string $errorMessage, public int $attempts, public DateTimeImmutable $occurredAt,) {}eventId ist deterministisch — runId:sourceOffset:idempotencyKey:status — was die Outbox-Deduplizierung möglich macht. toArray() serialisiert das Ereignis für den Transport; es trägt keine PDF-Bytes. JobTerminalStatus ist ein String-Enum: Committed (committed), DeadLettered (dead_lettered), Skipped (skipped).
ProcessingSummary
Abschnitt betitelt „ProcessingSummary“Unveränderliche Zähler, die von process() zurückgegeben werden: runId, sourceRead, fastForwardedByCheckpoint, skippedByIdempotency, windows, renderBatchCalls, renderRetries, commitReceipts, deadLettered, checkpointSaves und finalCommittedOffset (der finale terminale High-Watermark).
Codebeispiel — Schnellstart
Abschnitt betitelt „Codebeispiel — Schnellstart“Exactly-once-Objektspeicher-Übergabe in Isolation. Der In-Memory-NullObjectStorageClient steht stellvertretend für Ihren S3/GCS-Adapter; die Semantik, die Sie beobachten, ist diejenige, die ein Live-Adapter bewahren muss.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Stream\Storage\NullObjectStorageClient;use NextPDF\Enterprise\Stream\Storage\ObjectStorageCommitter;use NextPDF\Manifest\OutputObjectKey;use NextPDF\Pro\Stream\Exception\OutputCommitConflictException;use Symfony\Component\Clock\NativeClock;
$committer = new ObjectStorageCommitter( client: new NullObjectStorageClient(), // swap in your S3/GCS adapter scheme: 's3', clock: new NativeClock(),);
$target = new OutputObjectKey(scheme: 's3', container: 'invoices', key: '2026/07/inv-1001.pdf');$bytes = '%PDF-1.7 example-rendered-bytes';$sha = hash('sha256', $bytes);
$first = $committer->commit('inv-1001', $target, $bytes, $sha);$replay = $committer->commit('inv-1001', $target, $bytes, $sha); // crash-resume replay
printf("first : reuse=%s, %d bytes\n", var_export($first->idempotentReuse, true), $first->bytesWritten);printf("replay: reuse=%s\n", var_export($replay->idempotentReuse, true));
try { $divergent = '%PDF-1.7 different-bytes'; $committer->commit('inv-1001', $target, $divergent, hash('sha256', $divergent));} catch (OutputCommitConflictException $conflict) { echo 'conflict: ' . $conflict->specCode() . "\n"; // no silent clobber}Erwartete Ausgabe:
first : reuse=false, 31 bytesreplay: reuse=trueconflict: SPEC-COMMIT-409Codebeispiel — Produktion
Abschnitt betitelt „Codebeispiel — Produktion“Ein vollständiger absturzsicherer Lauf: dauerhafte Pro-Speicher, der Objektspeicher-Committer, eine dauerhafte Outbox und lizenzaufgelöstes Branding. Ein erneuter Lauf mit derselben runId nach einem Absturz springt vor und konvergiert.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Enterprise\Licensing\EntitlementEvaluator;use NextPDF\Enterprise\Stream\DocumentJobStreamProcessor;use NextPDF\Enterprise\Stream\Exception\StreamProcessorException;use NextPDF\Enterprise\Stream\FilesystemOutboxEmitter;use NextPDF\Enterprise\Stream\Storage\ObjectStorageCommitter;use NextPDF\Enterprise\Stream\StreamProcessorConfig;use NextPDF\Manifest\Render\SingleDocumentRenderer;use NextPDF\Manifest\RenderManifest;use NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore;use NextPDF\Pro\Stream\Dedup\FilesystemIdempotencyStore;use NextPDF\Pro\Stream\Engine\InProcessRenderEngine;use NextPDF\Pro\Stream\Retry\FilesystemDeadLetterStore;use NextPDF\Pro\Stream\Retry\RetryPolicy;use NextPDF\Pro\Stream\State\InMemoryKeyedStateStore;use Symfony\Component\Clock\NativeClock;
// Production requires a host-supplied adapter whose putIfAbsent() is a TRUE// atomic conditional create (S3 If-None-Match: *, GCS ifGenerationMatch: 0)// and whose shaOf() reads durable object state. NullObjectStorageClient is// for the quick start only - it keeps nothing across processes.$s3Client = new \Aws\S3\S3Client(['region' => 'eu-central-1', 'version' => 'latest']);$objectClient = new \Acme\Storage\S3ObjectStorageClient($s3Client); // implements ObjectStorageClientInterface
$stateDir = '/var/lib/nextpdf/stream';foreach (['checkpoints', 'idempotency', 'dead-letters', 'outbox'] as $sub) { if (!is_dir($stateDir . '/' . $sub)) { mkdir($stateDir . '/' . $sub, 0770, true); }}
// One manifest per JSONL line; the generator never materialises the batch.$manifests = (static function (string $path): Generator { $handle = fopen($path, 'rb'); if ($handle === false) { throw new RuntimeException('Cannot open job stream: ' . $path); } try { while (($line = fgets($handle)) !== false) { if (trim($line) !== '') { yield RenderManifest::fromJson(trim($line)); } } } finally { fclose($handle); }})('/var/spool/nextpdf/jobs.jsonl');
$license = null; // your licensing bootstrap yields a LicenseKey; null = evaluation branding
$processor = DocumentJobStreamProcessor::withBrandingFromLicense( engine: new InProcessRenderEngine(SingleDocumentRenderer::standalone()), // For a live bucket, implement ObjectStorageClientInterface over your S3/GCS SDK. committer: new ObjectStorageCommitter($objectClient, 's3', new NativeClock()), idempotency: new FilesystemIdempotencyStore($stateDir . '/idempotency'), checkpoints: new FilesystemCheckpointStore($stateDir . '/checkpoints'), state: new InMemoryKeyedStateStore(), // recomputable; durability not required here deadLetters: new FilesystemDeadLetterStore($stateDir . '/dead-letters'), retryPolicy: new RetryPolicy(maxAttempts: 3, baseDelayMs: 200, maxDelayMs: 5_000), clock: new NativeClock(), entitlementEvaluator: new EntitlementEvaluator(), license: $license, emitter: new FilesystemOutboxEmitter($stateDir . '/outbox'),);
$config = new StreamProcessorConfig( runId: 'nightly-invoices-2026-07-03', windowSize: 32, checkpointIntervalJobs: 100, crashSafe: true,);
try { $summary = $processor->process($manifests, $config);} catch (StreamProcessorException $e) { // Ambiguous commit or a non-durable collaborator: the finalized prefix is // checkpointed. Re-run the SAME runId; the committer converges by digest. fwrite(STDERR, 'Run aborted for safe resume: ' . $e->getMessage() . PHP_EOL); exit(1);}
printf( "run %s: read=%d committed=%d dedup-skipped=%d dead-lettered=%d checkpoints=%d final-offset=%d\n", $summary->runId, $summary->sourceRead, $summary->commitReceipts, $summary->skippedByIdempotency, $summary->deadLettered, $summary->checkpointSaves, $summary->finalCommittedOffset,);Beispielausgabe (die Zähler hängen von Ihrem Auftrags-Stream ab):
run nightly-invoices-2026-07-03: read=1200 committed=1187 dedup-skipped=13 dead-lettered=0 checkpoints=12 final-offset=1200Grenzfälle & Fallstricke
Abschnitt betitelt „Grenzfälle & Fallstricke“- Single-Writer pro
runIdliegt in Ihrer Verantwortung. Der Checkpoint-Speicher hat keine Lease und kein Compare-and-Swap. Zwei nebenläufige Writer auf einerrunIdliegen außerhalb des Vertrags; erzwingen Sie Exklusivität in Ihrem Scheduler. crashSafe: truescheitert schnell bei nicht-dauerhaften Kollaboratoren. Der Committer-, Checkpoint-, Idempotenz- und Dead-Letter-Speicher müssen alle denDurableCapability-Marker implementieren, sonst wirftprocess()eineStreamProcessorException, die die Verursacher benennt. Der Keyed-State-Speicher ist bewusst ausgenommen: verlorener Keyed-State wird ab dem Checkpoint vorwärts neu berechnet.windowSizemuss zur Engine passen. Ein Fenster größer alsmaxBatchSize()wirftInvalidArgumentException, bevor irgendeine Arbeit beginnt.- Eine mehrdeutige Übergabe bricht ab; ein Konflikt nicht.
SPEC-COMMIT-409ist ein deterministischer terminaler Konflikt: Das Element wird dead-lettered und der Lauf fährt fort. Jeder andere Übergabefehler ist mehrdeutig: Der finalisierte Präfix wird per Checkpoint gesichert und der Lauf wirft zur sicheren Wiederaufnahme. - Render-Fehler brechen den Lauf niemals ab. Ein
Failed-Ergebnis pro Element, ein erschöpftes Retry-Budget oder nicht brandbare Evaluations-Bytes lassen dieses Element dead-lettern und fahren fort. - Doppelte
jobId-Werte sind sicher; doppelte Arbeit wird überidempotencyKeyverschlüsselt. Ergebnisse werden über den eindeutigen Quell-Offset mit Elementen korreliert, niemals überjobId. Ein doppelter Idempotenzschlüssel wird selbst innerhalb desselben Barriere-Intervalls erkannt, vor dem erneuten Rendern. - Die Dauerhaftigkeit des Emitters entscheidet über die Ereignis-Semantik. Ein einfacher Callback-Emitter ist nur Beobachter und at-most-once über einen Absturz hinweg.
FilesystemOutboxEmittermacht die Outbox dauerhaft und dedup-verschlüsselt; die Relay-Auslieferung ist dann at-least-once, und nachgelagertes Exactly-once erfordert eine Konsumenten-Deduplizierung anhand dereventId. Sein Verzeichnis (wie das jedes Dateisystemspeichers) muss bereits existieren, sonst wirft der KonstruktorInvalidArgumentException. - Übersprungene Ereignisse sind standardmäßig aus. Setzen Sie
emitSkippedCompletions: true, um auch einSkipped-Terminalereignis für per Dedup kurzgeschlossene Elemente zu emittieren.
Sicherheitshinweise
Abschnitt betitelt „Sicherheitshinweise“- Ausgabeschlüssel scheitern fail-closed.
commit()bekräftigt erneut, dass der Zielschlüssel container-relativ-sicher ist: kein..-Traversal, kein absoluter Ausbruch, kein Nullbyte, kein eingebettetes Stream-Wrapper-Schema und kein Doppelpunkt (der den NTFS-Alternate-Data-Stream-Vektor schließt). Unsichere Schlüssel werfen, bevor irgendein Speicheraufruf erfolgt. - Integrität wird an der Grenze erneut verifiziert. Der Committer berechnet sha-256 über die tatsächlichen Bytes neu und weist einen Mismatch mit
CommitIntegrityExceptionab, sodass eine beschädigte Übergabe nicht stillschweigend landen kann. - Ereignisse tragen keinen Dokumentinhalt.
JobTerminalEventund die Outbox-Zeilen halten nur Bezeichner, Digests, Zeitstempel und Fehlerstrings. Fehlermeldungen können Engine-Diagnosen widerspiegeln; bereinigen Sie sie, und jedes tenant-identifizierendejobId-Schema, bevor Sie Outbox-Dateien an Drittanbieter-Senken versenden. - Evaluations-Ausgabe wird niemals ungebrandet veröffentlicht. Wenn Branding erforderlich ist und nicht angewendet werden kann, wird das Element dead-lettered statt übergeben.
- Cross-Writer-Exactly-once ist nur so stark wie Ihr Adapter. Wenn
putIfAbsent()kein echter atomarer Conditional-Write ist, degradiert die Garantie zur Single-Writer-Semantik. Objektspeicher-Anmeldedaten und Bucket-Richtlinie sind Host-Angelegenheiten; das Modul verwaltet sie nie.
Konformität
Abschnitt betitelt „Konformität“Kein veröffentlichter Standard definiert das Verhalten dieses Moduls. Die Exactly-once-, Checkpoint- und Outbox-Garantien auf dieser Seite sind Engineering-Verträge der NextPDF Enterprise API, hier als extern beobachtbares Verhalten dargelegt — sie sind keine Konformität zu oder Zertifizierung gegen irgendeinen Standard. Die interne Verwendung von SHA-256 als Integritäts-Digest ist ebenfalls Infrastruktur, keine Compliance-Aussage. Wie überall in NextPDF gilt: Unterstützung ist nicht Konformität, und Konformität ist nicht Zertifizierung. NextPDF hält keine Zertifizierung und gewährt keine; ob eine auf diesem Modul aufgebaute Bereitstellung Ihre regulatorischen oder vertraglichen Verpflichtungen erfüllt, ist eine Feststellung für Ihre Prüfer.
Verhaltensvertrag
Abschnitt betitelt „Verhaltensvertrag“Terminale Ereignisse sind nicht Teil der Checkpoint-Transaktion: Die Emission läuft nach checkpoint.save(), sodass die Outbox jedes emittierte Ereignis dauerhaft hält, aber über Abstürze hinweg kein vollständiges Ledger ist. Übergebene Objekte bleiben die Quelle der Wahrheit.
- Jeder Quell-Offset an oder unterhalb von
finalCommittedOffsethat genau einen Endzustand erreicht:Committed,SkippedoderDeadLettered. - Elemente werden in Quell-Offset-Reihenfolge finalisiert; die Barriere-Reihenfolge ist Übergabe, Checkpoint-Speicherung, Idempotenzmarken-Leerung, dann Ereignis-Emission.
- Ein erneuter Lauf mit derselben
runIdveröffentlicht niemals doppelt: per Checkpoint gesicherte Offsets springen vor, und byte-identische Re-Commits sind digest-verglichene No-ops mitidempotentReuse: true. - Ein frisches Objekt wird nur über das atomare bedingte Anlegen erstellt; abweichende Bytes an einem belegten Schlüssel ohne
overwritesind ein deterministischesSPEC-COMMIT-409-Dead-Letter, niemals ein Clobber. - Eine mehrdeutige Übergabe sichert den finalisierten Präfix per Checkpoint und bricht mit
StreamProcessorExceptionab; der fehlgeschlagene Offset wird nicht vorgerückt. - Ein
crashSafe-Lauf verweigert nicht-dauerhafte Committer-, Checkpoint-, Idempotenz- oder Dead-Letter-Kollaboratoren, bevor irgendeine Eingabe gelesen wird. - Ereignis-IDs sind eine reine Funktion von Lauf, Offset, Idempotenzschlüssel und Status, sodass eine dauerhafte Outbox höchstens eine Zeile pro Ereignis hält.
Core-Fallback
Abschnitt betitelt „Core-Fallback“NextPDF Core rendert ein Dokument nach dem anderen über den Writer und den Render-Manifest-Vertrag — siehe Writer. Core allein hat keine dauerhaften Auftrags-Streams, keine Checkpoint-Wiederaufnahme, keine Idempotenz-Deduplizierung, keine Objektspeicher-Übergabe und keine Terminalereignis-Outbox. NextPDF Pro fügt die dauerhafte, nebenläufige Render-Engine und die Single-Host-Dateisystemspeicher hinzu (Stream in Pro). Die hostübergreifende Hälfte — dieser Prozessor, der Objektspeicher-Committer und die dauerhafte Outbox — erfordert NextPDF Enterprise.
Publikationsgrenze
Abschnitt betitelt „Publikationsgrenze“Diese Seite dokumentiert nur extern beobachtbares Verhalten und die unterstützte öffentliche API-Oberfläche. Interne Namespace-Pfade, Hilfsklassen, Mechanismus-Tabellen, Runbook-Dateinamen und Ticket-Präfixe sind außerhalb des Umfangs.
Siehe auch
Abschnitt betitelt „Siehe auch“- Stream (Pro) — die In-Process-Hälfte: Render-Engine, Executors und lokale dauerhafte Speicher.
- Stream — Deep Reference — die vertragsstufige Referenz für die gemeinsamen Stream-Schnittstellen.
- Output Pipeline (Enterprise) — Batch-Orchestrierung über Pipeline-Manifeste.
- Testversion und Branding — wie Evaluations-Branding aufgelöst und angewendet wird.
- Dokumenterzeugung in hohem Volumen — das Szenario, für das dieses Modul existiert.
- NextPDF in der Produktion betreiben — Deployment-Haltung für langlaufende Worker.