Pro editie
Stream — Diepe referentie
In het kort
Sectie met titel “In het kort”Deze pagina documenteert de publieke contracten, klassen, methoden en faalmodi van het NextPDF\Pro\Stream-subsysteem die verder gaan dan de overzichtspagina. Elk type hieronder maakt deel uit van het gedocumenteerde publieke Pro-oppervlak.
Beschikbaarheid en licentie
Sectie met titel “Beschikbaarheid en licentie”Deze mogelijkheid wordt geleverd in NextPDF Pro (nextpdf/pro) en wordt geactiveerd met een licentie-envelope op Pro-niveau. Een deployment zonder die rechten laadt de klassen van de mogelijkheid niet. Vergelijk edities en vraag een licentie aan.
Er geldt geen licentievlag per feature; de code wordt geleverd met de Pro-editie. Aantal workers, batchgrootte, retry-budget en store-backend zijn runtimeparameters.
Architectuur: de bevroren engine-naad
Sectie met titel “Architectuur: de bevroren engine-naad”NextPDF\Pro\Stream\Engine\RenderEngineInterface is het contract tussen de throughput-engine en de document-job-streamprocessor. De engine implementeert het (en bezit concurrency, worker-pool-levenscyclus, backpressure, begrensd geheugen); de streamprocessor consumeert het (en bezit keyed state, dedup, retry, checkpoint en exactly-once commit). De engine retourneert bytes plus sha-256, nooit een gecommitte locatie — die side-effect-vrijheid is wat de processor in staat stelt om exactly once te stagen, committen en checkpointen.
public function renderBatch(array $manifests, array $variablesByJobId = []): array; // list<EngineRenderResult>, input orderpublic function maxBatchSize(): int; // int<1, max> backpressure hintpublic function isAvailable(): bool;$manifests is een list<RenderManifest> met een grootte van ten hoogste maxBatchSize(); $variablesByJobId mapt job-id op array<string, scalar> template-variabelen. Een mislukking per manifest is een Failed/Timeout-resultaat per item en breekt de batch nooit af.
Engines
Sectie met titel “Engines”NextPDF\Pro\Stream\Engine\InProcessRenderEngine
Sectie met titel “NextPDF\Pro\Stream\Engine\InProcessRenderEngine”Synchrone, single-process-baseline. Valideert elk manifest fail-closed via RenderManifestValidator (16 MiB inline-payload-cap, conformiteits-/handtekening-allow-lists, sha-256-content-hash-formaat, BCP-47-localesyntaxis) voordat het rendert via de Core-SingleDocumentRenderer. Een blokkerende validatiefout sluit kort naar EngineRenderResult::failed(jobId, 'SPEC-MANIFEST-INVALID', ...); een render-exception wordt 'SPEC-RENDER-EXCEPTION'. Constructor: __construct(SingleDocumentRenderer $renderer, int $maxBatchSize = 64, ?RenderManifestValidator $validator = null) — maxBatchSize < 1 werpt InvalidArgumentException. isAvailable() is altijd true.
NextPDF\Pro\Stream\Engine\ConcurrentRenderEngine
Sectie met titel “NextPDF\Pro\Stream\Engine\ConcurrentRenderEngine”final readonly, __construct(RenderUnitExecutorInterface $executor). Verpakt elk manifest in een geïndexeerde RenderUnit, draait ze door de executor en sorteert de voltooiingen opnieuw op index zodat de uitvoer byte-identiek is aan een sequentiële render. Een voltooiingsindex buiten [0, count) werpt RenderEngineException::unknownUnit(); een herhaalde index werpt duplicateResult(); een ontbrekende index werpt missingResult(). maxBatchSize() en isAvailable() delegeren naar de executor.
Executors
Sectie met titel “Executors”NextPDF\Pro\Stream\Engine\RenderUnitExecutorInterface
Sectie met titel “NextPDF\Pro\Stream\Engine\RenderUnitExecutorInterface”public function execute(array $units): iterable; // iterable<CompletedRenderUnit>, any orderpublic function maxBatchSize(): int;public function isAvailable(): bool;Implementaties mogen voltooiingen in elke volgorde yielden; ConcurrentRenderEngine herstelt de volgorde op index.
NextPDF\Pro\Stream\Engine\InlineRenderUnitExecutor
Sectie met titel “NextPDF\Pro\Stream\Engine\InlineRenderUnitExecutor”final readonly, __construct(RenderEngineInterface $inner). Rendert elke unit in volgorde via de inner engine — de deterministische correctheidsreferentie die een parallelle executor byte-voor-byte moet evenaren. Geen tijd, processen, threads of randomness.
NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor
Sectie met titel “NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor”final readonly. Verdeelt een batch over maximaal maxWorkers php-workersubprocessen (elk één chunk) die parallel renderen; de uitvoer is byte-identiek aan de inline-baseline. Constructor:
__construct( int $maxWorkers = 4, int $maxBatchSize = 64, ?string $phpBinary = null, ?string $workerScript = null, ?string $autoload = null, ?int $timeoutSeconds = 300, // null disables the wall-clock watchdog)Robuustheidscontract:
- Deadlock-vrij, Windows-veilig. Unit-payloads en -resultaten reizen via temp-bestanden, niet via pipes; de parent pollt
proc_get_status()en draineert een pipe pas tot EOF nadat een worker is afgesloten, zodat een worker de parent niet kan vastzetten. - Begrensde wachttijd.
timeoutSecondsbegrenst de hele parallelle render; bij verloop wordt elke nog draaiende worker beëindigd en wordt eenRenderEngineExceptiongeworpen. - Resource-hygiëne. Een
finallysluit pipes, doet een begrensde terminate-and-reap-poging op overlevende workers (graceful terminate → force-kill → reap; een child die niet binnen de begrensde grace tot stilstand wordt waargenomen, wordt achtergelaten in plaats van een onbepaalde blokkering te riskeren), en unlinkt elk temp-bestand op alle paden. - Vertrouwde correlatie. Elke worker moet exact zijn toegewezen indexset retourneren (geen ontbrekende, dubbele of vreemde index); de bytes van elk gerenderd resultaat worden opnieuw gehasht en gematcht tegen de door de worker gerapporteerde sha-256, en elke andere status dan
rendered/failedfaalt hard. Een render-mislukking per manifest is eenFailed-resultaat per unit; alleen een infrastructurele fout (niet-nul exit, onleesbare/verminkte uitvoer, timeout) laat de executor hard falen.
isAvailable() vereist dat zowel het autoload-bestand als het worker-script bestaan. Een niet-positieve grens of negatieve timeout werpt InvalidArgumentException.
Render-units en -resultaten
Sectie met titel “Render-units en -resultaten”NextPDF\Pro\Stream\Engine\RenderUnit
Sectie met titel “NextPDF\Pro\Stream\Engine\RenderUnit”final readonly — int<0, max> $index, RenderManifest $manifest, array<string, scalar> $variables. Correlatie verloopt via index, nooit via job-id (job-id’s zijn niet gegarandeerd uniek binnen een batch).
NextPDF\Pro\Stream\Engine\CompletedRenderUnit
Sectie met titel “NextPDF\Pro\Stream\Engine\CompletedRenderUnit”final readonly — int $index (untrusted, gevalideerd door de engine), EngineRenderResult $result.
NextPDF\Pro\Stream\Engine\EngineRenderResult
Sectie met titel “NextPDF\Pro\Stream\Engine\EngineRenderResult”final readonly. Velden: jobId, EngineRenderStatus $status, ?string $bytes, ?string $sha256, int $pageCount, ?string $errorCode, ?string $errorMessage, array<non-empty-string, float> $timings. Factories: rendered(jobId, bytes, sha256, pageCount, timings = []), failed(jobId, errorCode, errorMessage), timedOut(jobId, errorMessage) (code SPEC-ENGINE-TIMEOUT). isRendered() rapporteert de status. Een gerenderd resultaat draagt bytes en een digest, nooit een gecommitte locatie.
NextPDF\Pro\Stream\Engine\EngineRenderStatus
Sectie met titel “NextPDF\Pro\Stream\Engine\EngineRenderStatus”String-backed enum: Rendered, Failed, Timeout. isRetryable() is true alleen voor Timeout, zodat de caller een timeout als transiënt classificeert zonder de fout opnieuw te inspecteren.
NextPDF\Pro\Stream\Commit\OutputCommitterInterface
Sectie met titel “NextPDF\Pro\Stream\Commit\OutputCommitterInterface”public function commit( string $jobId, OutputObjectKey $target, string $bytes, string $sha256, bool $overwrite = false,): CommitReceipt;Exactly-once-publicatie: atomair, idempotent (een byte-identieke re-commit voert geen schrijfactie uit en retourneert een CommitReceipt met idempotentReuse = true — een verse receipt, niet de originele; zijn committedAt is de huidige klok), geen stille overschrijving en integriteitsgecontroleerd (de committer herberekent de digest). Faalmodi: CommitIntegrityException (de gedeclareerde sha-256 komt niet overeen met de bytes), OutputCommitConflictException (afwijkende bytes op een bezette sleutel met overwrite = false), UnsupportedTargetException (niet-ondersteund target-schema).
NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter
Sectie met titel “NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter”final readonly, implementeert OutputCommitterInterface, DurableCapability. __construct(string $rootDirectory, ?AtomicFileWriter $writer = null, ?ClockInterface $clock = null). Bedient alleen het file-schema; lost elk target op onder één geconfigureerde root en schrijft via een atomaire writer (O_EXCL temp → fsync → same-volume rename). De volledige kritieke sectie (inclusief het aanmaken van de parent-directory) draait onder een exclusieve flock op een per-root-lockbestand dat buiten de output-keyspace wordt gehouden, en de commit is fail-closed als de lock niet kan worden geopend of verkregen. Het weigert symlinked finale componenten en elke sleutel die een dubbele punt bevat (NTFS alternate-data-stream-vector). Cross-host concurrente exactly-once naar dezelfde sleutel vereist de duurzame Enterprise-committer. Een root die de systeem-temp-directory is of bevat, werpt InvalidArgumentException.
NextPDF\Pro\Stream\Commit\CommitReceipt
Sectie met titel “NextPDF\Pro\Stream\Commit\CommitReceipt”final readonly — jobId, OutputObjectKey $target, sha256, int<0, max> $bytesWritten, bool $idempotentReuse, DateTimeImmutable $committedAt. toArray() / fromArray() zijn volledig round-trippable (het target is gestructureerd, geen lossy URI); fromArray() is strikt en werpt InvalidArgumentException bij ontbrekende of misvormde velden.
Checkpoint
Sectie met titel “Checkpoint”NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface
Sectie met titel “NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface”load(string $runId): ?RunCheckpoint en save(RunCheckpoint $checkpoint): void (duurzaam en atomair — een reader ziet nooit een half geschreven checkpoint).
NextPDF\Pro\Stream\Checkpoint\RunCheckpoint
Sectie met titel “NextPDF\Pro\Stream\Checkpoint\RunCheckpoint”final readonly — runId, int<0, max> $committedOffset, array $keyedState, DateTimeImmutable $updatedAt; SCHEMA_VERSION = '1.0'. Factories start(runId, at) en advancedTo(committedOffset, keyedState, at). toArray()/toJson()/fromArray()/fromJson() serialiseren het; fromArray() vereist een niet-lege run-id en een geldige updated_at, wijst een incompatibele (niet-1.x) schema_version af, en normaliseert keyed state door op elke diepte alle niet-JSON-serialiseerbare waarden te laten vallen zodat herstelde state altijd herserialiseerbaar is. Bij herstel spoelt de processor vooruit voorbij committedOffset en herstelt het de keyed state; state die na de laatste barrière is gemuteerd, wordt voorwaarts herberekend, nooit een fout, omdat duurzame exactly-once afkomstig is van de digest-dedup van de committer.
NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore
Sectie met titel “NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore”final readonly, implementeert CheckpointStoreInterface, DurableCapability. Eén JSON-bestand per run, atomair geschreven. Run-id’s moeten overeenkomen met [A-Za-z0-9._-]+ en geen .. bevatten; een niet-bestaande directory werpt InvalidArgumentException.
Idempotentie-dedup
Sectie met titel “Idempotentie-dedup”NextPDF\Pro\Stream\Dedup\IdempotencyStoreInterface
Sectie met titel “NextPDF\Pro\Stream\Dedup\IdempotencyStoreInterface”isCommitted(IdempotencyKey $key): bool, markCommitted(IdempotencyKey $key, CommitReceipt $receipt): void, receiptFor(IdempotencyKey $key): ?CommitReceipt. Het snelle pad dat kortsluit voordat een replay wordt gerenderd; de digest-vergelijking van de committer blijft de duurzame garantie, zodat een verloren record in het slechtste geval een herrender verspilt die de committer dedupliceert.
InMemoryIdempotencyStore— single-run / testscope (verloren bij crash).FilesystemIdempotencyStore—DurableCapability; één atomair JSON-bestand per gecommitte sleutel (de geserialiseerde receipt), benoemd naar een hash van de sleutelwaarde. Marks zijn idempotent; een concurrente re-mark racet onschadelijk op één bestand. Een niet-bestaande directory werptInvalidArgumentException.
Retry en dead-letters
Sectie met titel “Retry en dead-letters”NextPDF\Pro\Stream\Retry\RetryPolicy
Sectie met titel “NextPDF\Pro\Stream\Retry\RetryPolicy”final readonly — positive-int $maxAttempts, positive-int $baseDelayMs, positive-int $maxDelayMs. __construct(int $maxAttempts = 3, int $baseDelayMs = 100, int $maxDelayMs = 30000) met invarianten maxAttempts >= 1 en 1 <= baseDelayMs <= maxDelayMs <= 7 days (anders InvalidArgumentException). Factories default() en none() (enkele poging). shouldRetry(int $attempt): bool. delayMsForAttempt(int $attempt): int<0, max> is deterministische exponentiële backoff baseDelayMs * 2^(attempt-1) begrensd op maxDelayMs (geen ingebouwde jitter; pas die toe op de call-site).
NextPDF\Pro\Stream\Retry\DeadLetterStoreInterface
Sectie met titel “NextPDF\Pro\Stream\Retry\DeadLetterStoreInterface”add(DeadLetterRecord $record): void, all(): list<DeadLetterRecord>, count(): int<0, max>.
NextPDF\Pro\Stream\Retry\DeadLetterRecord
Sectie met titel “NextPDF\Pro\Stream\Retry\DeadLetterRecord”final readonly — jobId, idempotencyKeyValue, positive-int $attempts, lastErrorCode, lastErrorMessage, DateTimeImmutable $failedAt, optioneel ?string $runId, optioneel int<1, max> $sourceOffset. dedupKey() is runId:sourceOffset wanneer beide bekend zijn, anders de idempotentie-sleutelwaarde. fromArray() parseert failed_at strikt als ATOM (en wijst relatieve of niet-ATOM-expressies af) zodat serialiseren/deserialiseren symmetrisch blijft.
InMemoryDeadLetterStore— single-run / testscope.FilesystemDeadLetterStore—DurableCapability; één atomair JSON-bestand per record, benoemd naar een SHA-256-hash van de dedup-sleutel (….dlq.json), zodat het opnieuw toevoegen van hetzelfde item bij hervatting idempotent is.all()leest records in deterministische (gesorteerde) volgorde en brengt een corrupt record aan het licht door te werpen;count()is een goedkope bestandstelling, geen geldigheidscontrole.
Keyed state
Sectie met titel “Keyed state”NextPDF\Pro\Stream\State\KeyedStateStoreInterface
Sectie met titel “NextPDF\Pro\Stream\State\KeyedStateStoreInterface”has, get, put, remove, clear, plus snapshot(): array en restore(array $snapshot): void voor de checkpointgrens. Waarden moeten JSON-serialiseerbaar zijn. Voor de standaard render-en-commit-workload wordt geen keyed state gebruikt; die bestaat voor aggregatie-/windowing-extensies. InMemoryKeyedStateStore is de single-run-implementatie; deze verliezen bij herstel is een semantische no-op voor de standaardworkload omdat exactly-once afkomstig is van de digest-dedup van de committer.
NextPDF\Pro\Stream\State\KeySelector
Sectie met titel “NextPDF\Pro\Stream\State\KeySelector”final readonly, __construct(string $tenantField = 'tenant_id', string $documentField = 'document_id'). keyFor(RenderManifest $manifest): non-empty-string leidt de partitiesleutel af uit manifestmetadata als rawurlencode(tenant):rawurlencode(document) (de encoding voorkomt dat ("a:b","c") botst met ("a","b:c")), met de job-id als fallback wanneer een van beide velden ontbreekt — zodat elk manifest oplost naar een stabiele, niet-lege sleutel.
Duurzaamheidsmarker en exceptions
Sectie met titel “Duurzaamheidsmarker en exceptions”NextPDF\Pro\Stream\DurableCapability is een marker-interface voor elke store/committer waarvan de state een procesherstart overleeft. Een crashveilige run vereist dat elke collaborator het implementeert, zodat hij fail-fast werkt in plaats van exactly-once te beloven die een in-memory-store niet kan nakomen.
Alle subsysteem-exceptions implementeren NextPDF\Pro\Stream\Exception\StreamException (extends Throwable), zodat een caller uniform catch (StreamException) kan gebruiken:
RenderEngineException(RuntimeException) — de executor heeft het batchcontract geschonden (onbekende, dubbele of ontbrekende unit; worker-fout; timeout).CommitIntegrityException(RuntimeException) — de gedeclareerde sha-256 komt niet overeen met de payload; spec-codeSPEC-COMMIT-422.OutputCommitConflictException(RuntimeException) — afwijkende bytes op een bezette sleutel met overwrite uitgeschakeld; spec-codeSPEC-COMMIT-409(blootgesteld viaspecCode()).UnsupportedTargetException(InvalidArgumentException) — een target-schema dat een committer niet kan bedienen.
Conformiteit
Sectie met titel “Conformiteit”De engine valideert manifesten tegen het Core-manifestmodel en produceert deterministische bytes plus sha-256-digests; de committer dwingt atomaire, integriteitsgecontroleerde, exactly-once-schrijfacties af. De module voert geen cryptografische bewerkingen uit buiten sha-256-content-digests en definieert geen FIPS-specifiek gedrag.
Randgevallen en valkuilen
Sectie met titel “Randgevallen en valkuilen”renderBatch()breekt nooit af bij een mislukking per manifest; inspecteer elkEngineRenderResult.ProcessPoolRenderUnitExecutorcorreleert strikt op index en hasht worker-bytes opnieuw; een buggy worker faalt hard in plaats van de uitvoer te corrumperen.LocalFilesystemCommitteris single-host; cross-host exactly-once vereist de duurzame Enterprise-committer.- Crashveilige runs moeten overal de
DurableCapability- (bestandssysteem-)stores gebruiken, niet de in-memory-varianten.
Publicatiegrens
Sectie met titel “Publicatiegrens”Deze pagina documenteert alleen extern waarneembaar gedrag en het ondersteunde publieke API-oppervlak. Interne namespace-paden, helper-klassen, mechanismetabellen, runbook-bestandsnamen en ticket-prefixen vallen buiten de scope.