Enterprise edisi
Stream: pemrosesan document-job
Sekilas pandang
Bagian berjudul “Sekilas pandang”NextPDF\Enterprise\Stream\DocumentJobStreamProcessor mengubah stream dari render manifest menjadi hasil yang durabel dan dapat dipertanggungjawabkan. Ia mengonsumsi sebuah iterable<RenderManifest> sebagai generator, merender jendela berbatas melalui render engine Pro, dan memfinalisasi setiap job dalam urutan sumber. Setiap job berakhir dalam tepat satu terminal state: output ter-commit, dikenali sebagai sudah ter-commit, atau dead-lettered. Progres di-checkpoint, sehingga run yang crash melanjutkan tanpa mem-publish ulang apa pun.
Cerita Stream terbagi ke dalam dua edisi, dan pembagian itu disengaja. Pro menyediakan render engine yang durabel dan konkuren serta store filesystem lokal single-host — separuh yang in-process. Enterprise menyediakan document-job stream processor ini plus bagian-bagian yang melintasi batas host: committer object-storage (ObjectStorageCommitter) dan outbox durabel untuk terminal event (FilesystemOutboxEmitter). Halaman Pro Stream menyatakan batas yang sama dari sisinya.
Ketersediaan & lisensi
Bagian berjudul “Ketersediaan & lisensi”Kapabilitas ini dikirim dalam NextPDF Enterprise (nextpdf/enterprise) dan aktif dengan envelope lisensi tier Enterprise. Sebuah deployment tanpa entitlement tersebut tidak memuat kelas-kelas kapabilitas ini. Bandingkan edisi dan dapatkan lisensi.
Instalasi
Bagian berjudul “Instalasi”composer require nextpdf/enterpriseKelas-kelas pada halaman ini berada di bawah NextPDF\Enterprise\Stream dan NextPDF\Enterprise\Stream\Storage. Mereka mengonsumsi kontrak Pro yang beku di NextPDF\Pro\Stream — interface engine, committer, checkpoint, idempotency, retry, dan dead-letter.
Tinjauan konseptual
Bagian berjudul “Tinjauan konseptual”Tugas processor adalah semantik pengiriman, bukan rendering. Ia mengelompokkan stream manifest ke dalam jendela source-offset yang tidak lebih besar dari batch size engine. Setiap jendela dirender melalui RenderEngineInterface::renderBatch(), dengan retry berbatas dan deterministik untuk timeout per-item. Kemudian setiap item difinalisasi dalam urutan source-offset menuju hasil akhir.
Batas exactly-once bersifat per item, dan ia berlabuh di committer, bukan pada koordinasi. Progres durabel adalah high-watermark offset berbasis-1: setiap offset pada atau di bawah checkpoint telah mencapai hasil akhir. Urutan barrier ditetapkan: commit byte-nya, majukan watermark, simpan checkpoint, flush idempotency mark yang di-buffer, lalu pancarkan terminal event. Crash antara commit dan checkpoint melakukan re-commit secara idempoten saat resume, karena committer membandingkan digest. Crash setelah checkpoint mem-fast-forward melewati offset, sehingga tidak ada yang di-publish dua kali.
ObjectStorageCommitter mengimplementasikan OutputCommitterInterface Pro terhadap sebuah object store melalui ObjectStorageClientInterface yang minimal. container pada target adalah bucket dan key-nya adalah object key. Meng-commit ulang byte yang identik adalah no-op yang dibandingkan-digest. Byte yang berbeda tanpa overwrite memunculkan konflik SPEC-COMMIT-409. Sebuah objek baru hanya pernah dibuat dengan conditional write atomik putIfAbsent(); kalah dalam race itu memicu loop re-read-and-resolve yang berbatas. Exactly-once lintas-writer karenanya berlaku persis sejauh putIfAbsent() adalah adapter Anda merupakan conditional write yang sesungguhnya — If-None-Match: * pada S3, ifGenerationMatch: 0 pada GCS. Siklus ini mengirim interface plus NullObjectStorageClient yang in-memory; adapter S3/GCS langsung disediakan host.
Terminal event menutup loop untuk sistem downstream. Setelah barrier checkpoint, processor mencoba memancarkan sebuah JobTerminalEvent untuk setiap job yang difinalisasi — identifier, status, receipt, detail error, jumlah percobaan, dan tidak pernah byte PDF apa pun. Dengan emitter callback biasa, pemancaran bersifat at-most-once: event setelah checkpoint dapat dilewati saat crash-resume. FilesystemOutboxEmitter membuat setiap event durabel begitu emit() berjalan: setiap event adalah satu file JSON atomik yang dinamai dengan hash dari eventId deterministiknya, sehingga memancarkan ulang setelah resume bersifat idempoten, sebuah relay mengirim at-least-once, dan konsumer melakukan dedup pada eventId. Satu batas tetap ada bagaimanapun juga: pemancaran terjadi setelah barrier checkpoint, sehingga crash antara checkpoint.save() dan emit() melewati terminal event item tersebut saat resume. Sistem downstream yang memerlukan ledger event yang lengkap sebaiknya merekonsiliasi terhadap objek yang ter-commit (store adalah sumber kebenaran), bukan terhadap outbox saja.
Lisensi disambungkan ke jalur output. Factory withBrandingFromLicense() me-resolve strategi evaluation-branding sekali per run dari lisensi. Lisensi berbayar me-resolve menjadi transform identitas. Lisensi evaluasi atau yang hilang mem-watermark setiap dokumen yang ter-commit, dan dokumen yang tidak dapat di-brand di-dead-letter — processor tidak pernah meng-commit byte evaluasi yang tak ber-brand.
Mengapa ia bekerja seperti ini
Bagian berjudul “Mengapa ia bekerja seperti ini”Keputusan yang menanggung beban adalah bahwa exactly-once bertumpu pada objek committer yang dibandingkan-digest dan dibuat secara kondisional — bukan pada distributed lock atau consensus. Conditional write pada object store adalah satu-satunya primitif atomik yang dibutuhkan desain ini, dan semua yang lain diizinkan untuk gagal dan pulih. Itulah sebabnya render engine harus tetap bebas efek-samping, sebabnya keyed state dan cache dedup dalam-run diperlakukan sebagai akselerasi yang dapat dihitung ulang, dan sebabnya commit yang ambigu membatalkan run alih-alih menebak: jalur resume berkonvergensi melalui perbandingan digest yang sama. Itu juga sebabnya single-writer per runId adalah persyaratan yang dinyatakan alih-alih lease yang dipaksakan — checkpoint store sengaja tetap sederhana, dan lapisan commit tetap menjadi jaring pengaman.
Latar belakang desain: High-volume document generation.
Permukaan API
Bagian berjudul “Permukaan API”DocumentJobStreamProcessor
Bagian berjudul “DocumentJobStreamProcessor”Host sebaiknya mengonstruksi melalui factory, sehingga kontrol license-to-branding tidak pernah dibiarkan tak tersambung:
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 adalah Symfony\Component\Clock\ClockInterface (backoff retry tidur melaluinya). Lisensi null me-resolve fail-closed menjadi evaluation branding.
Titik masuk tunggal memproses satu run dan mengembalikan counter-nya:
public function process(iterable $manifests, StreamProcessorConfig $config): ProcessingSummaryMelempar atau gagal dengan: NextPDF\Enterprise\Stream\Exception\StreamProcessorException ketika prakondisi crash-safety gagal (run crashSafe dengan kolaborator non-durabel) atau ketika sebuah commit ambigu; InvalidArgumentException ketika windowSize melampaui maxBatchSize() engine.
StreamProcessorConfig
Bagian berjudul “StreamProcessorConfig”public function __construct( public string $runId, int $windowSize = 32, int $checkpointIntervalJobs = 100, public bool $crashSafe = true, public bool $emitSkippedCompletions = false,)Melempar atau gagal dengan: InvalidArgumentException ketika windowSize atau checkpointIntervalJobs di bawah 1. $runId adalah identifier run single-writer yang stabil yang menjadi kunci checkpoint resume.
ObjectStorageCommitter
Bagian berjudul “ObjectStorageCommitter”public function __construct( private ObjectStorageClientInterface $client, private string $scheme, private ClockInterface $clock,) {}$scheme menamai skema target yang dilayani committer ini (misalnya s3 atau gcs); $clock di sini adalah Psr\Clock\ClockInterface.
public function commit( string $jobId, OutputObjectKey $target, string $bytes, string $sha256, bool $overwrite = false,): CommitReceiptMelempar atau gagal dengan: UnsupportedTargetException pada ketidakcocokan skema; RenderManifestException ketika target key bukan container-relative-safe; CommitIntegrityException ketika sha-256 yang dideklarasikan tidak cocok dengan byte-nya; OutputCommitConflictException (SPEC-COMMIT-409) pada byte yang berbeda tanpa overwrite; RuntimeException ketika race create tidak dapat berkonvergensi setelah 5 percobaan di bawah mutasi konkuren.
ObjectStorageClientInterface
Bagian berjudul “ObjectStorageClientInterface”Permukaan adapter minimal yang diimplementasikan oleh integrasi S3/GCS langsung:
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() harus merupakan conditional create atomik yang sesungguhnya (If-None-Match: * pada S3, ifGenerationMatch: 0 pada GCS) dan mengembalikan true hanya ketika panggilan ini menulis objek tersebut. put() adalah overwrite tanpa syarat yang digunakan semata-mata ketika manifest meminta overwrite.
JobCompletionEmitterInterface dan FilesystemOutboxEmitter
Bagian berjudul “JobCompletionEmitterInterface dan FilesystemOutboxEmitter”public function emit(JobTerminalEvent $event): void;Emitter bersifat opsional pada processor. Event hanya menyala setelah sebuah item difinalisasi secara durabel. FilesystemOutboxEmitter adalah implementasi durabel yang dikirim:
public function __construct(string $directory, ?AtomicFileWriter $writer = null)Melempar atau gagal dengan: InvalidArgumentException ketika direktori tidak ada; emit() melempar RuntimeException jika sebuah event tidak dapat di-JSON-encode. hasEvent(string $eventId): bool memeriksa outbox; count(): int melaporkan event yang belum terkirim.
JobTerminalEvent dan JobTerminalStatus
Bagian berjudul “JobTerminalEvent dan 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 bersifat deterministik — runId:sourceOffset:idempotencyKey:status — itulah yang membuat dedup outbox menjadi mungkin. toArray() menserialisasi event untuk transport; ia tidak membawa byte PDF. JobTerminalStatus adalah string enum: Committed (committed), DeadLettered (dead_lettered), Skipped (skipped).
ProcessingSummary
Bagian berjudul “ProcessingSummary”Counter immutable yang dikembalikan oleh process(): runId, sourceRead, fastForwardedByCheckpoint, skippedByIdempotency, windows, renderBatchCalls, renderRetries, commitReceipts, deadLettered, checkpointSaves, dan finalCommittedOffset (high-watermark terminal terakhir).
Contoh kode — Quick start
Bagian berjudul “Contoh kode — Quick start”Exactly-once object-storage commit secara terisolasi. NullObjectStorageClient yang in-memory menggantikan adapter S3/GCS Anda; semantik yang Anda amati adalah yang harus dipertahankan oleh adapter langsung.
<?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}Output yang diharapkan:
first : reuse=false, 31 bytesreplay: reuse=trueconflict: SPEC-COMMIT-409Contoh kode — Production
Bagian berjudul “Contoh kode — Production”Sebuah run crash-safe penuh: store Pro yang durabel, committer object-storage, outbox yang durabel, dan branding yang di-resolve dari lisensi. Menjalankan ulang runId yang sama setelah crash melakukan fast-forward dan berkonvergensi.
<?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,);Contoh output (counter bergantung pada job stream Anda):
run nightly-invoices-2026-07-03: read=1200 committed=1187 dedup-skipped=13 dead-lettered=0 checkpoints=12 final-offset=1200Edge case & jebakan
Bagian berjudul “Edge case & jebakan”- Single-writer per
runIdadalah tanggung jawab Anda. Checkpoint store tidak memiliki lease atau compare-and-swap. Dua writer konkuren pada saturunIdberada di luar kontrak; tegakkan eksklusivitas di scheduler Anda. crashSafe: truegagal cepat pada kolaborator non-durabel. Store committer, checkpoint, idempotency, dan dead-letter semuanya harus mengimplementasikan markerDurableCapability, atauprocess()melemparStreamProcessorExceptionyang menamai pelanggarnya. Keyed state store secara sengaja dikecualikan: keyed state yang hilang dihitung ulang dari checkpoint ke depan.windowSizeharus muat untuk engine. Sebuah jendela yang lebih besar darimaxBatchSize()melemparInvalidArgumentExceptionsebelum pekerjaan apa pun dimulai.- Commit yang ambigu membatalkan; konflik tidak.
SPEC-COMMIT-409adalah konflik terminal yang deterministik: item di-dead-letter dan run berlanjut. Kegagalan commit lain apa pun bersifat ambigu: prefix yang difinalisasi di-checkpoint dan run melempar untuk resume yang aman. - Kegagalan render tidak pernah membatalkan run. Hasil
Failedper-item, budget retry yang habis, atau byte evaluasi yang tak dapat di-brand semuanya men-dead-letter item tersebut dan berlanjut. - Nilai
jobIdyang duplikat aman; pekerjaan duplikat dikunci padaidempotencyKey. Hasil dikorelasikan ke item berdasarkan source offset yang unik, tidak pernah berdasarkanjobId. Sebuah idempotency key yang duplikat dikenali bahkan dalam interval barrier yang sama, sebelum me-render ulang. - Durabilitas emitter menentukan semantik event. Emitter callback biasa bersifat observer-only dan at-most-once melintasi crash.
FilesystemOutboxEmittermembuat outbox durabel dan ber-dedup-key; pengiriman relay kemudian bersifat at-least-once, dan exactly-once downstream memerlukan dedup konsumer padaeventId. Direktorinya (seperti setiap filesystem store) harus sudah ada, atau konstruktor melemparInvalidArgumentException. - Skipped event mati secara default. Setel
emitSkippedCompletions: trueuntuk juga memancarkan terminal eventSkippeduntuk item yang di-short-circuit oleh dedup.
Catatan keamanan
Bagian berjudul “Catatan keamanan”- Output key gagal secara tertutup.
commit()menegaskan ulang bahwa target key adalah container-relative-safe: tanpa traversal.., tanpa escape absolut, tanpa null byte, tanpa embedded stream-wrapper scheme, dan tanpa titik dua (yang menutup vektor NTFS alternate-data-stream). Key yang tidak aman melempar sebelum panggilan storage apa pun. - Integritas diverifikasi ulang di batas. Committer menghitung ulang sha-256 atas byte yang sebenarnya dan menolak ketidakcocokan dengan
CommitIntegrityException, sehingga handoff yang rusak tidak dapat mendarat secara diam-diam. - Event tidak membawa konten dokumen.
JobTerminalEventdan baris outbox hanya menyimpan identifier, digest, timestamp, dan string error. Pesan error dapat menggemakan diagnostik engine; bersihkan mereka, dan skemajobIdyang mengidentifikasi tenant apa pun, sebelum mengirim file outbox ke sink pihak ketiga. - Output evaluasi tidak pernah dipublikasikan tanpa brand. Ketika branding diperlukan dan tidak dapat diterapkan, item di-dead-letter alih-alih di-commit.
- Exactly-once lintas-writer hanya sekuat adapter Anda. Jika
putIfAbsent()bukan conditional write atomik yang sesungguhnya, jaminan itu merosot menjadi semantik single-writer. Kredensial object-store dan kebijakan bucket adalah urusan host; modul tidak pernah mengelolanya.
Konformansi
Bagian berjudul “Konformansi”Tidak ada standar terbit yang mendefinisikan perilaku modul ini. Jaminan exactly-once, checkpoint, dan outbox pada halaman ini adalah kontrak rekayasa dari API NextPDF Enterprise, dinyatakan di sini sebagai perilaku yang dapat diamati secara eksternal — mereka bukan konformansi terhadap, atau sertifikasi terhadap, standar apa pun. Penggunaan internal SHA-256 sebagai integrity digest juga merupakan plumbing, bukan klaim kepatuhan. Seperti di mana pun dalam NextPDF: dukungan bukanlah konformansi, dan konformansi bukanlah sertifikasi. NextPDF tidak memegang sertifikasi dan tidak memberikan apa pun; apakah sebuah deployment yang dibangun di atas modul ini memenuhi kewajiban regulatori atau kontraktual Anda adalah penentuan bagi asesor Anda.
Kontrak perilaku
Bagian berjudul “Kontrak perilaku”Terminal event bukan bagian dari transaksi checkpoint: pemancaran berjalan setelah checkpoint.save(), sehingga outbox menyimpan setiap event yang dipancarkan secara durabel tetapi bukan ledger yang lengkap melintasi crash. Objek yang ter-commit tetap menjadi sumber kebenaran.
- Setiap source offset pada atau di bawah
finalCommittedOffsettelah mencapai tepat satu hasil akhir:Committed,Skipped, atauDeadLettered. - Item difinalisasi dalam urutan source-offset; urutan barrier adalah commit, checkpoint save, idempotency-mark flush, lalu pemancaran event.
- Menjalankan ulang sebuah run dengan
runIdyang sama tidak pernah mem-publish ganda: offset yang ter-checkpoint di-fast-forward, dan re-commit yang byte-identik adalah no-op yang dibandingkan-digest denganidempotentReuse: true. - Sebuah objek baru hanya pernah dibuat melalui atomic conditional create; byte yang berbeda pada key yang terisi tanpa
overwriteadalah dead-letterSPEC-COMMIT-409yang deterministik, tidak pernah sebuah clobber. - Commit yang ambigu men-checkpoint prefix yang difinalisasi dan membatalkan dengan
StreamProcessorException; offset yang gagal tidak dimajukan. - Sebuah run
crashSafemenolak kolaborator committer, checkpoint, idempotency, atau dead-letter yang non-durabel sebelum membaca input apa pun. - Event id adalah fungsi murni dari run, offset, idempotency key, dan status, sehingga outbox yang durabel menyimpan paling banyak satu baris per event.
Fallback Core
Bagian berjudul “Fallback Core”NextPDF Core merender satu dokumen pada satu waktu melalui writer dan kontrak render-manifest — lihat Writer. Core saja tidak memiliki job stream yang durabel, tidak ada checkpoint resume, tidak ada idempotency dedup, tidak ada object-storage commit, dan tidak ada terminal-event outbox. NextPDF Pro menambahkan render engine yang durabel dan konkuren serta store filesystem single-host (Stream in Pro). Separuh lintas-host — processor ini, committer object-storage, dan outbox yang durabel — memerlukan NextPDF Enterprise.
Batas publikasi
Bagian berjudul “Batas publikasi”Halaman ini mendokumentasikan perilaku yang dapat diamati secara eksternal dan permukaan API publik yang didukung saja. Jalur namespace internal, kelas helper, tabel mekanisme, nama file runbook, dan prefix tiket berada di luar cakupan.
Lihat juga
Bagian berjudul “Lihat juga”- Stream (Pro) — separuh in-process: render engine, executor, dan store durabel lokal.
- Stream — Deep Reference — referensi tingkat-kontrak untuk interface Stream yang dibagikan.
- Output Pipeline (Enterprise) — orkestrasi batch atas manifest pipeline.
- Trial and branding — bagaimana evaluation branding di-resolve dan diterapkan.
- High-volume document generation — skenario yang menjadi alasan keberadaan modul ini.
- Operating NextPDF in production — postur deployment untuk worker yang berjalan lama.