Pro edición
Stream — Referencia detallada
De un vistazo
Sección titulada «De un vistazo»Esta página documenta los contratos públicos, las clases, los métodos y los modos de fallo del subsistema NextPDF\Pro\Stream más allá de la página de presentación. Cada tipo que figura a continuación forma parte de la superficie pública documentada de Pro.
Disponibilidad y licencia
Sección titulada «Disponibilidad y licencia»Esta capacidad se distribuye en NextPDF Pro (nextpdf/pro) y se activa con un sobre de licencia de nivel Pro. Un despliegue sin ese derecho no carga las clases de la capacidad. Compare las ediciones y obtenga una licencia.
No se aplica ningún indicador de licencia por característica; el código se distribuye con la edición Pro. El número de workers, el tamaño de lote, el presupuesto de reintentos y el backend de almacén son parámetros en tiempo de ejecución.
Arquitectura: la costura congelada del motor
Sección titulada «Arquitectura: la costura congelada del motor»NextPDF\Pro\Stream\Engine\RenderEngineInterface es el contrato entre el motor de rendimiento y el procesador de flujo de trabajos de documento. El motor lo implementa (poseyendo la concurrencia, el ciclo de vida del grupo de workers, la contrapresión y la memoria acotada); el procesador de flujo lo consume (poseyendo el estado con clave, la deduplicación, los reintentos, los puntos de control y la confirmación exactamente una vez). El motor devuelve bytes más sha-256, nunca una ubicación confirmada: esa ausencia de efectos secundarios es lo que permite al procesador preparar, confirmar y registrar el punto de control exactamente una vez.
public function renderBatch(array $manifests, array $variablesByJobId = []): array; // list<EngineRenderResult>, input orderpublic function maxBatchSize(): int; // int<1, max> backpressure hintpublic function isAvailable(): bool;$manifests es una list<RenderManifest> de tamaño máximo maxBatchSize(); $variablesByJobId asigna el id de trabajo a variables de plantilla array<string, scalar>. Un fallo por manifiesto es un resultado Failed/Timeout por elemento y nunca aborta el lote.
Motores
Sección titulada «Motores»NextPDF\Pro\Stream\Engine\InProcessRenderEngine
Sección titulada «NextPDF\Pro\Stream\Engine\InProcessRenderEngine»Línea de base síncrona, de un solo proceso. Valida cada manifiesto de fallo cerrado mediante RenderManifestValidator (tope de carga útil en línea de 16 MiB, listas de permitidos de conformidad/firma, formato de hash de contenido sha-256, sintaxis de configuración regional BCP-47) antes de renderizar a través del SingleDocumentRenderer de Core. Un error de validación bloqueante hace un cortocircuito a EngineRenderResult::failed(jobId, 'SPEC-MANIFEST-INVALID', ...); una excepción de renderizado se convierte en 'SPEC-RENDER-EXCEPTION'. Constructor: __construct(SingleDocumentRenderer $renderer, int $maxBatchSize = 64, ?RenderManifestValidator $validator = null) — maxBatchSize < 1 lanza InvalidArgumentException. isAvailable() es siempre true.
NextPDF\Pro\Stream\Engine\ConcurrentRenderEngine
Sección titulada «NextPDF\Pro\Stream\Engine\ConcurrentRenderEngine»final readonly, __construct(RenderUnitExecutorInterface $executor). Envuelve cada manifiesto en una RenderUnit indexada, los ejecuta a través del ejecutor y vuelve a ordenar las finalizaciones por índice para que la salida sea idéntica byte a byte a un renderizado secuencial. Un índice de finalización fuera de [0, count) lanza RenderEngineException::unknownUnit(); un índice repetido lanza duplicateResult(); un índice ausente lanza missingResult(). maxBatchSize() y isAvailable() delegan en el ejecutor.
Ejecutores
Sección titulada «Ejecutores»NextPDF\Pro\Stream\Engine\RenderUnitExecutorInterface
Sección titulada «NextPDF\Pro\Stream\Engine\RenderUnitExecutorInterface»public function execute(array $units): iterable; // iterable<CompletedRenderUnit>, any orderpublic function maxBatchSize(): int;public function isAvailable(): bool;Las implementaciones pueden producir las finalizaciones en cualquier orden; ConcurrentRenderEngine restaura el orden por índice.
NextPDF\Pro\Stream\Engine\InlineRenderUnitExecutor
Sección titulada «NextPDF\Pro\Stream\Engine\InlineRenderUnitExecutor»final readonly, __construct(RenderEngineInterface $inner). Renderiza cada unidad en orden a través del motor interno: la referencia de corrección determinista que un ejecutor paralelo debe igualar byte a byte. Sin tiempo, procesos, hilos ni aleatoriedad.
NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor
Sección titulada «NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor»final readonly. Distribuye un lote entre hasta maxWorkers subprocesos worker php (un fragmento cada uno) que renderizan en paralelo; la salida es idéntica byte a byte a la línea de base en línea. 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)Contrato de robustez:
- Sin interbloqueos, seguro en Windows. Las cargas útiles de las unidades y los resultados viajan a través de archivos temporales, no de tuberías; el padre sondea
proc_get_status()y solo drena una tubería hasta EOF después de que un worker haya salido, de modo que un worker no puede atascar al padre. - Espera acotada.
timeoutSecondslimita todo el renderizado paralelo; al expirar, cada worker aún en ejecución se termina y se lanza unaRenderEngineException. - Higiene de recursos. Un
finallycierra las tuberías, hace un intento acotado de terminar y recoger a los workers supervivientes (terminación grácil → forzado de cierre → recogida; un hijo que no se observa detenerse dentro del periodo de gracia acotado se abandona en lugar de arriesgar un bloqueo indefinido) y elimina cada archivo temporal en todas las rutas. - Correlación confiable. Cada worker debe devolver exactamente su conjunto de índices asignado (sin índices ausentes, duplicados ni ajenos); los bytes de cada resultado renderizado se vuelven a hashear y se cotejan con el sha-256 informado por el worker, y cualquier estado distinto de
rendered/failedprovoca un fallo grave. Un fallo de renderizado por manifiesto es un resultadoFailedpor unidad; solo un fallo de infraestructura (salida distinta de cero, salida ilegible o corrupta, timeout) provoca un fallo grave del ejecutor.
isAvailable() requiere que existan tanto el archivo de autoload como el script de worker. Un límite no positivo o un timeout negativo lanza InvalidArgumentException.
Unidades y resultados de renderizado
Sección titulada «Unidades y resultados de renderizado»NextPDF\Pro\Stream\Engine\RenderUnit
Sección titulada «NextPDF\Pro\Stream\Engine\RenderUnit»final readonly — int<0, max> $index, RenderManifest $manifest, array<string, scalar> $variables. La correlación es por index, nunca por id de trabajo (los ids de trabajo no tienen garantizada la unicidad dentro de un lote).
NextPDF\Pro\Stream\Engine\CompletedRenderUnit
Sección titulada «NextPDF\Pro\Stream\Engine\CompletedRenderUnit»final readonly — int $index (no confiable, validado por el motor), EngineRenderResult $result.
NextPDF\Pro\Stream\Engine\EngineRenderResult
Sección titulada «NextPDF\Pro\Stream\Engine\EngineRenderResult»final readonly. Campos: jobId, EngineRenderStatus $status, ?string $bytes, ?string $sha256, int $pageCount, ?string $errorCode, ?string $errorMessage, array<non-empty-string, float> $timings. Fábricas: rendered(jobId, bytes, sha256, pageCount, timings = []), failed(jobId, errorCode, errorMessage), timedOut(jobId, errorMessage) (código SPEC-ENGINE-TIMEOUT). isRendered() informa del estado. Un resultado renderizado transporta bytes y un resumen, nunca una ubicación confirmada.
NextPDF\Pro\Stream\Engine\EngineRenderStatus
Sección titulada «NextPDF\Pro\Stream\Engine\EngineRenderStatus»Enum respaldado por cadena: Rendered, Failed, Timeout. isRetryable() es true solo para Timeout, de modo que el llamante clasifica un timeout como transitorio sin reinspeccionar el error.
Confirmación
Sección titulada «Confirmación»NextPDF\Pro\Stream\Commit\OutputCommitterInterface
Sección titulada «NextPDF\Pro\Stream\Commit\OutputCommitterInterface»public function commit( string $jobId, OutputObjectKey $target, string $bytes, string $sha256, bool $overwrite = false,): CommitReceipt;Publicación exactamente una vez: atómica, idempotente (una reconfirmación idéntica byte a byte no realiza ninguna escritura y devuelve un CommitReceipt con idempotentReuse = true —un recibo nuevo, no el original—; su committedAt es el reloj actual), sin sobrescritura silenciosa y con integridad comprobada (el confirmador recalcula el resumen). Modos de fallo: CommitIntegrityException (el sha-256 declarado no coincide con los bytes), OutputCommitConflictException (bytes divergentes en una clave ocupada con overwrite = false), UnsupportedTargetException (esquema de destino no admitido).
NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter
Sección titulada «NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter»final readonly, implementa OutputCommitterInterface, DurableCapability. __construct(string $rootDirectory, ?AtomicFileWriter $writer = null, ?ClockInterface $clock = null). Solo atiende el esquema file; resuelve cada destino bajo una única raíz configurada y escribe a través de un writer atómico (temporal O_EXCL → fsync → renombrado en el mismo volumen). Toda la sección crítica (incluida la creación del directorio padre) se ejecuta bajo un flock exclusivo sobre un archivo de bloqueo por raíz mantenido fuera del espacio de claves de salida, y la confirmación es de fallo cerrado si el bloqueo no se puede abrir o adquirir. Rechaza los componentes finales con enlaces simbólicos y cualquier clave que contenga dos puntos (vector de flujo de datos alternativo de NTFS). La concurrencia entre hosts exactamente una vez a la misma clave requiere el confirmador duradero de Enterprise. Una raíz que sea o contenga el directorio temporal del sistema lanza InvalidArgumentException.
NextPDF\Pro\Stream\Commit\CommitReceipt
Sección titulada «NextPDF\Pro\Stream\Commit\CommitReceipt»final readonly — jobId, OutputObjectKey $target, sha256, int<0, max> $bytesWritten, bool $idempotentReuse, DateTimeImmutable $committedAt. toArray() / fromArray() son plenamente reversibles (el destino es estructurado, no un URI con pérdida); fromArray() es estricto y lanza InvalidArgumentException ante campos ausentes o malformados.
Punto de control
Sección titulada «Punto de control»NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface
Sección titulada «NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface»load(string $runId): ?RunCheckpoint y save(RunCheckpoint $checkpoint): void (duradero y atómico: un lector nunca ve un punto de control escrito a medias).
NextPDF\Pro\Stream\Checkpoint\RunCheckpoint
Sección titulada «NextPDF\Pro\Stream\Checkpoint\RunCheckpoint»final readonly — runId, int<0, max> $committedOffset, array $keyedState, DateTimeImmutable $updatedAt; SCHEMA_VERSION = '1.0'. Fábricas start(runId, at) y advancedTo(committedOffset, keyedState, at). toArray()/toJson()/fromArray()/fromJson() lo serializan; fromArray() requiere un id de ejecución no vacío y un updated_at válido, rechaza un schema_version incompatible (no 1.x) y normaliza el estado con clave descartando cualquier valor no serializable a JSON en todas las profundidades, de modo que el estado recuperado siempre sea reserializable. En la recuperación, el procesador avanza rápidamente más allá de committedOffset y restaura el estado con clave; el estado mutado tras la última barrera se recalcula hacia delante, nunca es un error, porque la garantía duradera de exactamente una vez proviene de la deduplicación por resumen del confirmador.
NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore
Sección titulada «NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore»final readonly, implementa CheckpointStoreInterface, DurableCapability. Un archivo JSON por ejecución, escrito de forma atómica. Los ids de ejecución deben coincidir con [A-Za-z0-9._-]+ y no contener ..; un directorio inexistente lanza InvalidArgumentException.
Deduplicación por idempotencia
Sección titulada «Deduplicación por idempotencia»NextPDF\Pro\Stream\Dedup\IdempotencyStoreInterface
Sección titulada «NextPDF\Pro\Stream\Dedup\IdempotencyStoreInterface»isCommitted(IdempotencyKey $key): bool, markCommitted(IdempotencyKey $key, CommitReceipt $receipt): void, receiptFor(IdempotencyKey $key): ?CommitReceipt. La ruta rápida que hace un cortocircuito antes de renderizar una repetición; la comparación de resúmenes del confirmador sigue siendo la garantía duradera, de modo que un registro perdido a lo sumo malgasta un re-renderizado que el confirmador deduplica.
InMemoryIdempotencyStore— alcance de una sola ejecución / pruebas (se pierde en una caída).FilesystemIdempotencyStore—DurableCapability; un archivo JSON atómico por clave confirmada (el recibo serializado), nombrado por un hash del valor de la clave. Las marcas son idempotentes; una remarcación concurrente compite de forma inofensiva sobre un único archivo. Un directorio inexistente lanzaInvalidArgumentException.
Reintentos y mensajes muertos
Sección titulada «Reintentos y mensajes muertos»NextPDF\Pro\Stream\Retry\RetryPolicy
Sección titulada «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) con las invariantes maxAttempts >= 1 y 1 <= baseDelayMs <= maxDelayMs <= 7 days (de lo contrario InvalidArgumentException). Fábricas default() y none() (un solo intento). shouldRetry(int $attempt): bool. delayMsForAttempt(int $attempt): int<0, max> es retroceso exponencial determinista baseDelayMs * 2^(attempt-1) limitado a maxDelayMs (sin jitter incorporado; aplíquelo en el punto de llamada).
NextPDF\Pro\Stream\Retry\DeadLetterStoreInterface
Sección titulada «NextPDF\Pro\Stream\Retry\DeadLetterStoreInterface»add(DeadLetterRecord $record): void, all(): list<DeadLetterRecord>, count(): int<0, max>.
NextPDF\Pro\Stream\Retry\DeadLetterRecord
Sección titulada «NextPDF\Pro\Stream\Retry\DeadLetterRecord»final readonly — jobId, idempotencyKeyValue, positive-int $attempts, lastErrorCode, lastErrorMessage, DateTimeImmutable $failedAt, opcional ?string $runId, opcional int<1, max> $sourceOffset. dedupKey() es runId:sourceOffset cuando ambos se conocen, y en caso contrario el valor de la clave de idempotencia. fromArray() analiza failed_at estrictamente como ATOM (rechazando expresiones relativas o no ATOM), de modo que serializar/deserializar siga siendo simétrico.
InMemoryDeadLetterStore— alcance de una sola ejecución / pruebas.FilesystemDeadLetterStore—DurableCapability; un archivo JSON atómico por registro, nombrado por un hash SHA-256 de la clave de deduplicación (….dlq.json), de modo que volver a añadir el mismo elemento al reanudar sea idempotente.all()lee los registros en orden determinista (ordenado) y revela un registro corrupto lanzando una excepción;count()es un recuento de archivos barato, no una comprobación de validez.
Estado con clave
Sección titulada «Estado con clave»NextPDF\Pro\Stream\State\KeyedStateStoreInterface
Sección titulada «NextPDF\Pro\Stream\State\KeyedStateStoreInterface»has, get, put, remove, clear, más snapshot(): array y restore(array $snapshot): void para el límite del punto de control. Los valores deben ser serializables a JSON. Para la carga de trabajo predeterminada de renderizar y confirmar no se usa ningún estado con clave; existe para extensiones de agregación/ventanas. InMemoryKeyedStateStore es la implementación de una sola ejecución; perderlo en la recuperación es un no-op semántico para la carga de trabajo predeterminada, porque el exactamente una vez proviene de la deduplicación por resumen del confirmador.
NextPDF\Pro\Stream\State\KeySelector
Sección titulada «NextPDF\Pro\Stream\State\KeySelector»final readonly, __construct(string $tenantField = 'tenant_id', string $documentField = 'document_id'). keyFor(RenderManifest $manifest): non-empty-string deriva la clave de partición a partir de los metadatos del manifiesto como rawurlencode(tenant):rawurlencode(document) (la codificación impide que ("a:b","c") colisione con ("a","b:c")), recurriendo al id de trabajo cuando falta cualquiera de los campos, de modo que cada manifiesto se resuelva en una clave estable y no vacía.
Marcador de durabilidad y excepciones
Sección titulada «Marcador de durabilidad y excepciones»NextPDF\Pro\Stream\DurableCapability es una interfaz marcadora para cualquier almacén/confirmador cuyo estado sobreviva al reinicio de un proceso. Una ejecución a prueba de caídas requiere que todos los colaboradores la implementen, de modo que falle rápidamente en lugar de prometer un exactamente una vez que un almacén en memoria no puede mantener.
Todas las excepciones del subsistema implementan NextPDF\Pro\Stream\Exception\StreamException (extiende Throwable), de modo que un llamante pueda hacer catch (StreamException) de forma uniforme:
RenderEngineException(RuntimeException) — el ejecutor violó el contrato del lote (unidad desconocida, duplicada o ausente; fallo de worker; timeout).CommitIntegrityException(RuntimeException) — el sha-256 declarado no coincide con la carga útil; código de especificaciónSPEC-COMMIT-422.OutputCommitConflictException(RuntimeException) — bytes divergentes en una clave ocupada con la sobrescritura deshabilitada; código de especificaciónSPEC-COMMIT-409(expuesto mediantespecCode()).UnsupportedTargetException(InvalidArgumentException) — esquema de destino que un confirmador no puede atender.
Conformidad
Sección titulada «Conformidad»El motor valida los manifiestos frente al modelo de manifiesto de Core y produce bytes deterministas más resúmenes sha-256; el confirmador impone escrituras atómicas, con integridad comprobada y exactamente una vez. El módulo no realiza operaciones criptográficas más allá de los resúmenes de contenido sha-256 y no define ningún comportamiento específico de FIPS.
Casos límite y trampas
Sección titulada «Casos límite y trampas»renderBatch()nunca aborta ante un fallo por manifiesto; inspeccione cadaEngineRenderResult.ProcessPoolRenderUnitExecutorcorrelaciona estrictamente por índice y vuelve a hashear los bytes del worker; un worker defectuoso provoca un fallo grave en lugar de corromper la salida.LocalFilesystemCommitteres de un solo host; el exactamente una vez entre hosts necesita el confirmador duradero de Enterprise.- Las ejecuciones a prueba de caídas deben usar los almacenes
DurableCapability(sistema de archivos) en todo momento, no las variantes en memoria.
Límite de publicación
Sección titulada «Límite de publicación»Esta página documenta únicamente el comportamiento observable externamente y la superficie de API pública compatible. Las rutas de espacio de nombres internas, las clases auxiliares, las tablas de mecanismos, los nombres de archivo de runbook y los prefijos de ticket quedan fuera de alcance.