Pro edisi
Stream — Referensi Mendalam
Sekilas pandang
Bagian berjudul “Sekilas pandang”Halaman ini mendokumentasikan kontrak, kelas, metode, dan mode kegagalan publik dari subsistem NextPDF\Pro\Stream di luar yang ada pada halaman tinjauan. Setiap tipe di bawah ini merupakan bagian dari permukaan publik Pro yang terdokumentasi.
Ketersediaan & lisensi
Bagian berjudul “Ketersediaan & lisensi”Kapabilitas ini dikirim dalam NextPDF Pro (nextpdf/pro) dan aktif dengan envelope lisensi tier Pro. Deployment tanpa entitlement tersebut tidak memuat kelas-kelas kapabilitas ini. Bandingkan edisi dan dapatkan lisensi.
Tidak ada flag lisensi per-fitur yang berlaku; kode dikirim bersama edisi Pro. Jumlah worker, ukuran batch, anggaran retry, dan backend store adalah parameter runtime.
Arsitektur: seam mesin yang beku
Bagian berjudul “Arsitektur: seam mesin yang beku”NextPDF\Pro\Stream\Engine\RenderEngineInterface adalah kontrak antara mesin throughput dan prosesor stream document-job. Mesin mengimplementasikannya (memegang konkurensi, siklus hidup worker-pool, backpressure, memori terbatas); prosesor stream mengonsumsinya (memegang keyed state, dedup, retry, checkpoint, dan commit exactly-once). Mesin mengembalikan byte ditambah sha-256, tidak pernah lokasi yang sudah di-commit — kebebasan efek-samping itulah yang memungkinkan prosesor melakukan staging, commit, dan checkpoint tepat sekali.
public function renderBatch(array $manifests, array $variablesByJobId = []): array; // list<EngineRenderResult>, input orderpublic function maxBatchSize(): int; // int<1, max> backpressure hintpublic function isAvailable(): bool;$manifests adalah list<RenderManifest> berukuran paling banyak maxBatchSize(); $variablesByJobId memetakan job id ke variabel templat array<string, scalar>. Kegagalan per-manifes adalah hasil Failed/Timeout per-item dan tidak pernah membatalkan batch.
Engines
Bagian berjudul “Engines”NextPDF\Pro\Stream\Engine\InProcessRenderEngine
Bagian berjudul “NextPDF\Pro\Stream\Engine\InProcessRenderEngine”Baseline sinkron, satu-proses. Memvalidasi setiap manifes secara fail-closed melalui RenderManifestValidator (batas payload inline 16 MiB, allow-list konformansi/tanda tangan, format content-hash sha-256, sintaks locale BCP-47) sebelum me-render melalui Core SingleDocumentRenderer. Galat validasi yang memblokir melakukan short-circuit ke EngineRenderResult::failed(jobId, 'SPEC-MANIFEST-INVALID', ...); eksepsi render menjadi 'SPEC-RENDER-EXCEPTION'. Konstruktor: __construct(SingleDocumentRenderer $renderer, int $maxBatchSize = 64, ?RenderManifestValidator $validator = null) — maxBatchSize < 1 memunculkan InvalidArgumentException. isAvailable() selalu true.
NextPDF\Pro\Stream\Engine\ConcurrentRenderEngine
Bagian berjudul “NextPDF\Pro\Stream\Engine\ConcurrentRenderEngine”final readonly, __construct(RenderUnitExecutorInterface $executor). Membungkus setiap manifes dalam RenderUnit berindeks, menjalankannya melalui executor, dan menyortir ulang penyelesaian berdasarkan indeks sehingga keluaran identik secara byte dengan render sekuensial. Indeks penyelesaian di luar [0, count) memunculkan RenderEngineException::unknownUnit(); indeks yang berulang memunculkan duplicateResult(); indeks yang hilang memunculkan missingResult(). maxBatchSize() dan isAvailable() mendelegasikan ke executor.
Executors
Bagian berjudul “Executors”NextPDF\Pro\Stream\Engine\RenderUnitExecutorInterface
Bagian berjudul “NextPDF\Pro\Stream\Engine\RenderUnitExecutorInterface”public function execute(array $units): iterable; // iterable<CompletedRenderUnit>, any orderpublic function maxBatchSize(): int;public function isAvailable(): bool;Implementasi boleh meng-yield penyelesaian dalam urutan apa pun; ConcurrentRenderEngine memulihkan urutan berdasarkan indeks.
NextPDF\Pro\Stream\Engine\InlineRenderUnitExecutor
Bagian berjudul “NextPDF\Pro\Stream\Engine\InlineRenderUnitExecutor”final readonly, __construct(RenderEngineInterface $inner). Me-render setiap unit secara berurutan melalui mesin dalam — referensi kebenaran deterministik yang harus dipadankan executor paralel byte demi byte. Tanpa waktu, proses, thread, atau keacakan.
NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor
Bagian berjudul “NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor”final readonly. Mendistribusikan sebuah batch ke hingga maxWorkers subproses worker php (satu chunk masing-masing) yang me-render secara paralel; keluaran identik secara byte dengan baseline inline. Konstruktor:
__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)Kontrak ketangguhan:
- Bebas deadlock, aman di Windows. Payload dan hasil unit melintas melalui berkas temp, bukan pipe; parent melakukan polling
proc_get_status()dan hanya menguras pipe hingga EOF setelah worker keluar, sehingga worker tidak dapat memacetkan parent. - Tunggu terbatas.
timeoutSecondsmembatasi keseluruhan render paralel; saat kedaluwarsa, setiap worker yang masih berjalan diterminasi dan sebuahRenderEngineExceptiondimunculkan. - Higiene sumber daya. Sebuah
finallymenutup pipe, melakukan upaya terminate-and-reap terbatas pada worker yang masih bertahan (terminate anggun → force-kill → reap; anak yang tidak teramati berhenti dalam masa tenggang terbatas ditinggalkan alih-alih mengambil risiko blokir tak terbatas), dan menghapus tautan setiap berkas temp di semua jalur. - Korelasi tepercaya. Setiap worker harus mengembalikan tepat himpunan indeks yang ditugaskan kepadanya (tanpa indeks yang hilang, duplikat, atau asing); byte setiap hasil render di-hash ulang dan dicocokkan terhadap sha-256 yang dilaporkan worker, dan status apa pun selain
rendered/failedgagal-keras. Kegagalan render per-manifes adalah hasilFailedper-unit; hanya kesalahan infrastruktural (exit non-nol, keluaran yang tak terbaca/rusak, timeout) yang menggagalkan-keras executor.
isAvailable() mensyaratkan baik berkas autoload maupun skrip worker ada. Batas non-positif atau timeout negatif memunculkan InvalidArgumentException.
Render unit dan hasil
Bagian berjudul “Render unit dan hasil”NextPDF\Pro\Stream\Engine\RenderUnit
Bagian berjudul “NextPDF\Pro\Stream\Engine\RenderUnit”final readonly — int<0, max> $index, RenderManifest $manifest, array<string, scalar> $variables. Korelasi dilakukan berdasarkan index, tidak pernah berdasarkan job id (job id tidak dijamin unik dalam satu batch).
NextPDF\Pro\Stream\Engine\CompletedRenderUnit
Bagian berjudul “NextPDF\Pro\Stream\Engine\CompletedRenderUnit”final readonly — int $index (tidak tepercaya, divalidasi oleh mesin), EngineRenderResult $result.
NextPDF\Pro\Stream\Engine\EngineRenderResult
Bagian berjudul “NextPDF\Pro\Stream\Engine\EngineRenderResult”final readonly. Field: jobId, EngineRenderStatus $status, ?string $bytes, ?string $sha256, int $pageCount, ?string $errorCode, ?string $errorMessage, array<non-empty-string, float> $timings. Factory: rendered(jobId, bytes, sha256, pageCount, timings = []), failed(jobId, errorCode, errorMessage), timedOut(jobId, errorMessage) (kode SPEC-ENGINE-TIMEOUT). isRendered() melaporkan status. Hasil yang ter-render membawa byte dan sebuah digest, tidak pernah lokasi yang sudah di-commit.
NextPDF\Pro\Stream\Engine\EngineRenderStatus
Bagian berjudul “NextPDF\Pro\Stream\Engine\EngineRenderStatus”Enum berbasis string: Rendered, Failed, Timeout. isRetryable() bernilai true hanya untuk Timeout, sehingga pemanggil mengklasifikasikan timeout sebagai transien tanpa memeriksa ulang galatnya.
Commit
Bagian berjudul “Commit”NextPDF\Pro\Stream\Commit\OutputCommitterInterface
Bagian berjudul “NextPDF\Pro\Stream\Commit\OutputCommitterInterface”public function commit( string $jobId, OutputObjectKey $target, string $bytes, string $sha256, bool $overwrite = false,): CommitReceipt;Publikasi exactly-once: atomik, idempoten (commit ulang yang identik secara byte tidak melakukan penulisan dan mengembalikan CommitReceipt dengan idempotentReuse = true — sebuah receipt baru, bukan yang asli; committedAt-nya adalah jam saat ini), tanpa clobber diam-diam, dan terperiksa integritasnya (committer menghitung ulang digest). Mode kegagalan: CommitIntegrityException (sha-256 yang dideklarasikan tidak cocok dengan byte), OutputCommitConflictException (byte yang berbeda pada key yang sudah terisi dengan overwrite = false), UnsupportedTargetException (skema target yang tidak didukung).
NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter
Bagian berjudul “NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter”final readonly, mengimplementasikan OutputCommitterInterface, DurableCapability. __construct(string $rootDirectory, ?AtomicFileWriter $writer = null, ?ClockInterface $clock = null). Hanya melayani skema file; meresolusi setiap target di bawah satu root yang dikonfigurasi dan menulis melalui writer atomik (temp O_EXCL → fsync → rename satu-volume). Seluruh seksi kritis (termasuk pembuatan direktori-induk) berjalan di bawah flock eksklusif pada berkas kunci per-root yang disimpan di luar keyspace keluaran, dan commit bersifat fail-closed jika kunci tidak dapat dibuka atau diperoleh. Ia menolak komponen final yang ber-symlink dan key apa pun yang mengandung titik dua (vektor NTFS alternate-data-stream). Exactly-once konkuren lintas-host ke key yang sama membutuhkan committer Enterprise yang durable. Root yang merupakan atau memuat direktori temp sistem memunculkan InvalidArgumentException.
NextPDF\Pro\Stream\Commit\CommitReceipt
Bagian berjudul “NextPDF\Pro\Stream\Commit\CommitReceipt”final readonly — jobId, OutputObjectKey $target, sha256, int<0, max> $bytesWritten, bool $idempotentReuse, DateTimeImmutable $committedAt. toArray() / fromArray() sepenuhnya dapat di-round-trip (target bersifat terstruktur, bukan URI yang lossy); fromArray() bersifat ketat dan memunculkan InvalidArgumentException pada field yang hilang atau cacat.
Checkpoint
Bagian berjudul “Checkpoint”NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface
Bagian berjudul “NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface”load(string $runId): ?RunCheckpoint dan save(RunCheckpoint $checkpoint): void (durable dan atomik — pembaca tidak pernah melihat checkpoint yang setengah-tertulis).
NextPDF\Pro\Stream\Checkpoint\RunCheckpoint
Bagian berjudul “NextPDF\Pro\Stream\Checkpoint\RunCheckpoint”final readonly — runId, int<0, max> $committedOffset, array $keyedState, DateTimeImmutable $updatedAt; SCHEMA_VERSION = '1.0'. Factory start(runId, at) dan advancedTo(committedOffset, keyedState, at). toArray()/toJson()/fromArray()/fromJson() menserialisasikannya; fromArray() mensyaratkan run id yang tidak kosong dan updated_at yang valid, menolak schema_version yang tidak kompatibel (non-1.x), dan menormalkan keyed state dengan membuang nilai apa pun yang tidak dapat diserialisasi-JSON di setiap kedalaman sehingga state yang dipulihkan selalu dapat diserialisasi ulang. Pada pemulihan, prosesor mempercepat-maju melewati committedOffset dan memulihkan keyed state; state yang dimutasi setelah barrier terakhir dihitung-ulang ke depan, tidak pernah sebuah galat, karena exactly-once durable berasal dari dedup digest milik committer.
NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore
Bagian berjudul “NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore”final readonly, mengimplementasikan CheckpointStoreInterface, DurableCapability. Satu berkas JSON per run, ditulis secara atomik. Run id harus cocok dengan [A-Za-z0-9._-]+ dan tidak mengandung ..; direktori yang tidak ada memunculkan InvalidArgumentException.
Idempotency dedup
Bagian berjudul “Idempotency dedup”NextPDF\Pro\Stream\Dedup\IdempotencyStoreInterface
Bagian berjudul “NextPDF\Pro\Stream\Dedup\IdempotencyStoreInterface”isCommitted(IdempotencyKey $key): bool, markCommitted(IdempotencyKey $key, CommitReceipt $receipt): void, receiptFor(IdempotencyKey $key): ?CommitReceipt. Jalur cepat yang melakukan short-circuit sebelum me-render sebuah replay; perbandingan digest milik committer tetap menjadi jaminan durable, sehingga rekaman yang hilang paling buruk hanya menyia-nyiakan render ulang yang didedup oleh committer.
InMemoryIdempotencyStore— cakupan single-run / pengujian (hilang saat crash).FilesystemIdempotencyStore—DurableCapability; satu berkas JSON atomik per key yang sudah di-commit (receipt terserialisasi), dinamai berdasarkan hash dari nilai key. Tanda bersifat idempoten; re-mark konkuren berlomba tanpa bahaya pada satu berkas. Direktori yang tidak ada memunculkanInvalidArgumentException.
Retry dan dead-letter
Bagian berjudul “Retry dan dead-letter”NextPDF\Pro\Stream\Retry\RetryPolicy
Bagian berjudul “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) dengan invarian maxAttempts >= 1 dan 1 <= baseDelayMs <= maxDelayMs <= 7 days (jika tidak, InvalidArgumentException). Factory default() dan none() (satu upaya). shouldRetry(int $attempt): bool. delayMsForAttempt(int $attempt): int<0, max> adalah exponential backoff deterministik baseDelayMs * 2^(attempt-1) dibatasi pada maxDelayMs (tanpa jitter bawaan; terapkan di sisi pemanggil).
NextPDF\Pro\Stream\Retry\DeadLetterStoreInterface
Bagian berjudul “NextPDF\Pro\Stream\Retry\DeadLetterStoreInterface”add(DeadLetterRecord $record): void, all(): list<DeadLetterRecord>, count(): int<0, max>.
NextPDF\Pro\Stream\Retry\DeadLetterRecord
Bagian berjudul “NextPDF\Pro\Stream\Retry\DeadLetterRecord”final readonly — jobId, idempotencyKeyValue, positive-int $attempts, lastErrorCode, lastErrorMessage, DateTimeImmutable $failedAt, opsional ?string $runId, opsional int<1, max> $sourceOffset. dedupKey() adalah runId:sourceOffset ketika keduanya diketahui, jika tidak nilai idempotency key. fromArray() mem-parsing failed_at secara ketat sebagai ATOM (menolak ekspresi relatif atau non-ATOM) sehingga serialise/deserialise tetap simetris.
InMemoryDeadLetterStore— cakupan single-run / pengujian.FilesystemDeadLetterStore—DurableCapability; satu berkas JSON atomik per rekaman, dinamai berdasarkan hash SHA-256 dari dedup key (….dlq.json), sehingga menambahkan item yang sama kembali saat resume bersifat idempoten.all()membaca rekaman dalam urutan deterministik (tersortir) dan memunculkan rekaman yang korup dengan melempar;count()adalah penghitungan berkas yang murah, bukan pemeriksaan validitas.
Keyed state
Bagian berjudul “Keyed state”NextPDF\Pro\Stream\State\KeyedStateStoreInterface
Bagian berjudul “NextPDF\Pro\Stream\State\KeyedStateStoreInterface”has, get, put, remove, clear, ditambah snapshot(): array dan restore(array $snapshot): void untuk batas checkpoint. Nilai harus dapat diserialisasi-JSON. Untuk workload render-and-commit baku, tidak ada keyed state yang digunakan; ia ada untuk ekstensi agregasi/windowing. InMemoryKeyedStateStore adalah implementasi single-run; kehilangannya saat pemulihan adalah no-op semantik untuk workload baku karena exactly-once berasal dari dedup digest milik committer.
NextPDF\Pro\Stream\State\KeySelector
Bagian berjudul “NextPDF\Pro\Stream\State\KeySelector”final readonly, __construct(string $tenantField = 'tenant_id', string $documentField = 'document_id'). keyFor(RenderManifest $manifest): non-empty-string menurunkan partition key dari metadata manifes sebagai rawurlencode(tenant):rawurlencode(document) (encoding ini mencegah ("a:b","c") bertabrakan dengan ("a","b:c")), beralih ke job id ketika salah satu field tidak ada — sehingga setiap manifes resolusi ke key yang stabil dan tidak kosong.
Durability marker dan eksepsi
Bagian berjudul “Durability marker dan eksepsi”NextPDF\Pro\Stream\DurableCapability adalah marker interface untuk store/committer apa pun yang state-nya bertahan melewati restart proses. Run yang crash-safe mensyaratkan setiap kolaborator mengimplementasikannya, sehingga ia gagal cepat alih-alih menjanjikan exactly-once yang tidak dapat dipertahankan oleh store in-memory.
Semua eksepsi subsistem mengimplementasikan NextPDF\Pro\Stream\Exception\StreamException (memperluas Throwable), sehingga pemanggil dapat melakukan catch (StreamException) secara seragam:
RenderEngineException(RuntimeException) — executor melanggar kontrak batch (unit yang tidak dikenal, duplikat, atau hilang; kesalahan worker; timeout).CommitIntegrityException(RuntimeException) — sha-256 yang dideklarasikan tidak cocok dengan payload; kode specSPEC-COMMIT-422.OutputCommitConflictException(RuntimeException) — byte yang berbeda pada key yang sudah terisi dengan overwrite dimatikan; kode specSPEC-COMMIT-409(diekspos melaluispecCode()).UnsupportedTargetException(InvalidArgumentException) — skema target yang tidak dapat dilayani committer.
Kesesuaian
Bagian berjudul “Kesesuaian”Mesin memvalidasi manifes terhadap model manifes Core dan menghasilkan byte deterministik ditambah digest sha-256; committer menegakkan penulisan yang atomik, terperiksa integritasnya, dan exactly-once. Modul tidak melakukan operasi kriptografis di luar digest konten sha-256 dan tidak mendefinisikan perilaku spesifik-FIPS.
Kasus tepi & jebakan
Bagian berjudul “Kasus tepi & jebakan”renderBatch()tidak pernah membatalkan pada kegagalan per-manifes; periksa setiapEngineRenderResult.ProcessPoolRenderUnitExecutormengorelasikan secara ketat berdasarkan indeks dan me-hash ulang byte worker; worker yang bermasalah gagal-keras alih-alih merusak keluaran.LocalFilesystemCommitterbersifat satu-host; exactly-once lintas-host membutuhkan committer Enterprise yang durable.- Run yang crash-safe harus menggunakan store
DurableCapability(filesystem) secara menyeluruh, bukan varian in-memory.
Batas publikasi
Bagian berjudul “Batas publikasi”Halaman ini mendokumentasikan hanya perilaku yang teramati secara eksternal dan permukaan API publik yang didukung. Jalur namespace internal, kelas helper, tabel mekanisme, nama berkas runbook, dan prefiks tiket berada di luar cakupan.