Pro エディション
Stream — 詳細リファレンス
このページでは、概要ページの範囲を超えて、NextPDF\Pro\Stream サブシステムの公開コントラクト、クラス、メソッド、失敗モードを記述します。以下のすべての型は、文書化された Pro の公開サーフェスの一部です。
提供状況とライセンス
「提供状況とライセンス」という見出しのセクションこの機能は NextPDF Pro(nextpdf/pro)に同梱されており、Pro ティアのライセンスエンベロープで有効化されます。そのエンタイトルメントを持たないデプロイメントでは、この機能のクラスはロードされません。エディションを比較してライセンスを入手。
機能ごとのライセンスフラグは適用されません。コードは Pro エディションに同梱されています。ワーカー数、バッチサイズ、リトライ予算、ストアバックエンドはランタイムのパラメーターです。
アーキテクチャ: 凍結されたエンジンのシーム
「アーキテクチャ: 凍結されたエンジンのシーム」という見出しのセクションNextPDF\Pro\Stream\Engine\RenderEngineInterface は、スループットエンジンとドキュメントジョブのストリームプロセッサーの間のコントラクトです。エンジンがこれを実装し(並行性、ワーカープールのライフサイクル、バックプレッシャー、境界付きメモリを所有)、ストリームプロセッサーがこれを消費します(キー付き状態、重複排除、リトライ、チェックポイント、exactly-once コミットを所有)。エンジンはバイト列に sha-256 を加えたものを返し、コミット済みの場所は決して返しません — その副作用のなさこそが、プロセッサーがちょうど一度だけステージング、コミット、チェックポイントを行えるようにします。
public function renderBatch(array $manifests, array $variablesByJobId = []): array; // list<EngineRenderResult>, input orderpublic function maxBatchSize(): int; // int<1, max> backpressure hintpublic function isAvailable(): bool;$manifests は最大で maxBatchSize() のサイズの list<RenderManifest> です。$variablesByJobId は、ジョブ ID を array<string, scalar> のテンプレート変数に対応付けます。マニフェスト単位の失敗は項目ごとの Failed/Timeout の結果であり、バッチを中断させることは決してありません。
NextPDF\Pro\Stream\Engine\InProcessRenderEngine
「NextPDF\Pro\Stream\Engine\InProcessRenderEngine」という見出しのセクション同期的・単一プロセスのベースライン。各マニフェストを RenderManifestValidator を通じてフェイルクローズドで検証してから(16 MiB のインラインペイロード上限、conformance/signature の許可リスト、sha-256 のコンテンツハッシュ形式、BCP-47 のロケール構文)、Core の SingleDocumentRenderer を通じてレンダリングします。ブロッキングする検証エラーは EngineRenderResult::failed(jobId, 'SPEC-MANIFEST-INVALID', ...) へショートカットし、レンダー例外は 'SPEC-RENDER-EXCEPTION' になります。コンストラクター: __construct(SingleDocumentRenderer $renderer, int $maxBatchSize = 64, ?RenderManifestValidator $validator = null) — maxBatchSize < 1 は InvalidArgumentException をスローします。isAvailable() は常に true です。
NextPDF\Pro\Stream\Engine\ConcurrentRenderEngine
「NextPDF\Pro\Stream\Engine\ConcurrentRenderEngine」という見出しのセクションfinal readonly、__construct(RenderUnitExecutorInterface $executor)。各マニフェストをインデックス付きの RenderUnit でラップし、それらをエグゼキューターを通じて実行し、完了をインデックスで再ソートして、出力がシーケンシャルなレンダリングとバイト単位で同一になるようにします。[0, count) の外側にある完了インデックスは RenderEngineException::unknownUnit() をスローし、重複したインデックスは duplicateResult() を、欠落したインデックスは missingResult() をスローします。maxBatchSize() と isAvailable() はエグゼキューターに委譲します。
エグゼキューター
「エグゼキューター」という見出しのセクションNextPDF\Pro\Stream\Engine\RenderUnitExecutorInterface
「NextPDF\Pro\Stream\Engine\RenderUnitExecutorInterface」という見出しのセクションpublic function execute(array $units): iterable; // iterable<CompletedRenderUnit>, any orderpublic function maxBatchSize(): int;public function isAvailable(): bool;実装は完了を任意の順序で yield してよく、ConcurrentRenderEngine がインデックスで順序を復元します。
NextPDF\Pro\Stream\Engine\InlineRenderUnitExecutor
「NextPDF\Pro\Stream\Engine\InlineRenderUnitExecutor」という見出しのセクションfinal readonly、__construct(RenderEngineInterface $inner)。各ユニットを内部エンジンを通じて順番にレンダリングします — 並列エグゼキューターがバイト単位で一致しなければならない、決定論的な正しさのリファレンスです。時間、プロセス、スレッド、ランダム性はありません。
NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor
「NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor」という見出しのセクションfinal readonly。バッチを最大 maxWorkers 個の php ワーカーサブプロセス(それぞれ 1 チャンク)に分散し、それらが並列でレンダリングします。出力はインラインのベースラインとバイト単位で同一です。コンストラクター:
__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)堅牢性の契約:
- デッドロックフリー、Windows-safe。 ユニットのペイロードと結果は、パイプではなく一時ファイルを通じて行き来します。親は
proc_get_status()をポーリングし、ワーカーが終了した後にのみパイプを EOF まで排出するため、ワーカーが親をくさび止めすることはできません。 - 境界付きの待機。
timeoutSecondsは並列レンダー全体に上限を設けます。期限切れになると、まだ実行中のすべてのワーカーが終了され、RenderEngineExceptionがスローされます。 - リソースの衛生。
finallyがパイプを閉じ、生き残ったワーカーに対して境界付きの終了・刈り取りの試行を行い(graceful terminate → force-kill → reap。境界付きの猶予時間内に停止が観測されなかった子は、無期限のブロックのリスクを冒すよりは放棄されます)、すべての経路で各一時ファイルをアンリンクします。 - 信頼できる相関付け。 各ワーカーは、割り当てられたインデックスセットをちょうど返さなければなりません(欠落、重複、外来のインデックスはなし)。レンダリングされた各結果のバイト列は再ハッシュされ、ワーカーが報告した sha-256 と照合され、
rendered/failed以外のいずれのステータスもハードに失敗します。マニフェスト単位のレンダー失敗はユニットごとのFailedの結果です。インフラ的な障害(非ゼロ終了、読み取り不能/文字化けした出力、タイムアウト)のみがエグゼキューターをハードに失敗させます。
isAvailable() は、autoload ファイルとワーカースクリプトの両方が存在することを必要とします。非正の境界または負のタイムアウトは InvalidArgumentException をスローします。
レンダーユニットと結果
「レンダーユニットと結果」という見出しのセクションNextPDF\Pro\Stream\Engine\RenderUnit
「NextPDF\Pro\Stream\Engine\RenderUnit」という見出しのセクションfinal readonly — int<0, max> $index、RenderManifest $manifest、array<string, scalar> $variables。相関付けは index によって行われ、ジョブ ID によっては決して行われません(ジョブ ID はバッチ内で一意であることが保証されません)。
NextPDF\Pro\Stream\Engine\CompletedRenderUnit
「NextPDF\Pro\Stream\Engine\CompletedRenderUnit」という見出しのセクションfinal readonly — int $index(信頼できない、エンジンが検証)、EngineRenderResult $result。
NextPDF\Pro\Stream\Engine\EngineRenderResult
「NextPDF\Pro\Stream\Engine\EngineRenderResult」という見出しのセクションfinal readonly。フィールド: jobId、EngineRenderStatus $status、?string $bytes、?string $sha256、int $pageCount、?string $errorCode、?string $errorMessage、array<non-empty-string, float> $timings。ファクトリー: rendered(jobId, bytes, sha256, pageCount, timings = [])、failed(jobId, errorCode, errorMessage)、timedOut(jobId, errorMessage)(コード SPEC-ENGINE-TIMEOUT)。isRendered() はステータスを報告します。レンダリングされた結果は、バイト列とダイジェストを運び、コミット済みの場所は決して運びません。
NextPDF\Pro\Stream\Engine\EngineRenderStatus
「NextPDF\Pro\Stream\Engine\EngineRenderStatus」という見出しのセクション文字列で裏付けられた enum: Rendered、Failed、Timeout。isRetryable() は Timeout に対してのみ true であるため、呼び出し元はエラーを再検査せずにタイムアウトを一時的なものとして分類します。
NextPDF\Pro\Stream\Commit\OutputCommitterInterface
「NextPDF\Pro\Stream\Commit\OutputCommitterInterface」という見出しのセクションpublic function commit( string $jobId, OutputObjectKey $target, string $bytes, string $sha256, bool $overwrite = false,): CommitReceipt;Exactly-once の公開: アトミック、冪等(バイト単位で同一の再コミットは書き込みを行わず、idempotentReuse = true を持つ CommitReceipt を返します — 元のものではなく、新しいレシートです。その committedAt は現在のクロックです)、サイレントな上書きなし、整合性チェック済み(コミッターがダイジェストを再計算)です。失敗モード: CommitIntegrityException(宣言された sha-256 がバイト列と一致しない)、OutputCommitConflictException(overwrite = false で使用中のキーに異なるバイト列)、UnsupportedTargetException(サポートされていないターゲットスキーム)。
NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter
「NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter」という見出しのセクションfinal readonly、OutputCommitterInterface, DurableCapability を実装します。__construct(string $rootDirectory, ?AtomicFileWriter $writer = null, ?ClockInterface $clock = null)。file スキームのみを扱います。すべてのターゲットを 1 つの構成済みルートの下で解決し、アトミックライター(O_EXCL temp → fsync → 同一ボリュームでのリネーム)を通じて書き込みます。クリティカルセクション全体(親ディレクトリの作成を含む)は、出力キースペースの外側に保持されたルートごとのロックファイルに対する排他的な flock の下で実行され、ロックを開けないか取得できない場合、コミットはフェイルクローズドです。シンボリックリンクされた最終コンポーネントと、コロンを含むあらゆるキー(NTFS の代替データストリームベクター)を拒否します。同一キーに対するクロスホストの並行 exactly-once には、永続的な Enterprise コミッターが必要です。システムの一時ディレクトリであるか、それを含むルートは InvalidArgumentException をスローします。
NextPDF\Pro\Stream\Commit\CommitReceipt
「NextPDF\Pro\Stream\Commit\CommitReceipt」という見出しのセクションfinal readonly — jobId、OutputObjectKey $target、sha256、int<0, max> $bytesWritten、bool $idempotentReuse、DateTimeImmutable $committedAt。toArray() / fromArray() は完全にラウンドトリップ可能です(ターゲットは構造化されており、損失を伴う URI ではありません)。fromArray() は厳格であり、欠落または不正なフィールドに対して InvalidArgumentException をスローします。
チェックポイント
「チェックポイント」という見出しのセクションNextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface
「NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface」という見出しのセクションload(string $runId): ?RunCheckpoint と save(RunCheckpoint $checkpoint): void(永続的かつアトミック — リーダーは書き込み途中のチェックポイントを決して見ません)。
NextPDF\Pro\Stream\Checkpoint\RunCheckpoint
「NextPDF\Pro\Stream\Checkpoint\RunCheckpoint」という見出しのセクションfinal readonly — runId、int<0, max> $committedOffset、array $keyedState、DateTimeImmutable $updatedAt。SCHEMA_VERSION = '1.0'。ファクトリー start(runId, at) と advancedTo(committedOffset, keyedState, at)。toArray()/toJson()/fromArray()/fromJson() がこれをシリアライズします。fromArray() は、空でない run id と有効な updated_at を必要とし、互換性のない(1.x でない)schema_version を拒否し、すべての深さで JSON シリアライズ不可能な値を取り除くことでキー付き状態を正規化するため、復元された状態は常に再シリアライズ可能です。復旧時、プロセッサーは committedOffset を早送りで通過し、キー付き状態を復元します。最後のバリア以降に変更された状態は前方へ再計算され、決してエラーにはなりません。なぜなら、永続的な exactly-once はコミッターのダイジェスト重複排除から来るからです。
NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore
「NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore」という見出しのセクションfinal readonly、CheckpointStoreInterface, DurableCapability を実装します。実行ごとに 1 つの JSON ファイルをアトミックに書き込みます。run id は [A-Za-z0-9._-]+ に一致し、.. を含んではなりません。存在しないディレクトリは InvalidArgumentException をスローします。
冪等性による重複排除
「冪等性による重複排除」という見出しのセクションNextPDF\Pro\Stream\Dedup\IdempotencyStoreInterface
「NextPDF\Pro\Stream\Dedup\IdempotencyStoreInterface」という見出しのセクションisCommitted(IdempotencyKey $key): bool、markCommitted(IdempotencyKey $key, CommitReceipt $receipt): void、receiptFor(IdempotencyKey $key): ?CommitReceipt。リプレイをレンダリングする前にショートカットするファストパスです。コミッターのダイジェスト比較が永続的な保証であり続けるため、レコードが失われても、最悪の場合に発生するのはコミッターが重複排除する無駄な再レンダリングだけです。
InMemoryIdempotencyStore— 単一実行/テストスコープ(クラッシュ時に失われます)。FilesystemIdempotencyStore—DurableCapability。コミット済みキーごとに 1 つのアトミックな JSON ファイル(シリアライズされたレシート)を、キー値のハッシュで命名します。マークは冪等であり、並行する再マークは 1 つのファイル上で無害に競合します。存在しないディレクトリはInvalidArgumentExceptionをスローします。
リトライとデッドレター
「リトライとデッドレター」という見出しのセクションNextPDF\Pro\Stream\Retry\RetryPolicy
「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)。不変条件は maxAttempts >= 1 と 1 <= baseDelayMs <= maxDelayMs <= 7 days(さもなければ InvalidArgumentException)です。ファクトリー default() と none()(単一試行)。shouldRetry(int $attempt): bool。delayMsForAttempt(int $attempt): int<0, max> は、maxDelayMs で上限を設けた決定論的な指数バックオフ baseDelayMs * 2^(attempt-1) です(組み込みのジッターはありません。呼び出し側で適用してください)。
NextPDF\Pro\Stream\Retry\DeadLetterStoreInterface
「NextPDF\Pro\Stream\Retry\DeadLetterStoreInterface」という見出しのセクションadd(DeadLetterRecord $record): void、all(): list<DeadLetterRecord>、count(): int<0, max>。
NextPDF\Pro\Stream\Retry\DeadLetterRecord
「NextPDF\Pro\Stream\Retry\DeadLetterRecord」という見出しのセクションfinal readonly — jobId、idempotencyKeyValue、positive-int $attempts、lastErrorCode、lastErrorMessage、DateTimeImmutable $failedAt、オプションの ?string $runId、オプションの int<1, max> $sourceOffset。dedupKey() は、両方が分かっている場合は runId:sourceOffset であり、そうでなければ冪等性キー値です。fromArray() は failed_at を ATOM として厳格に解析する(相対表現や非 ATOM 表現を拒否する)ため、シリアライズ/デシリアライズが対称に保たれます。
InMemoryDeadLetterStore— 単一実行/テストスコープ。FilesystemDeadLetterStore—DurableCapability。レコードごとに 1 つのアトミックな JSON ファイルを、重複排除キーの SHA-256 ハッシュ(….dlq.json)で命名するため、再開時に同じ項目を再追加しても冪等です。all()はレコードを決定論的な(ソートされた)順序で読み取り、破損したレコードをスローによって露出させます。count()は妥当性チェックではなく安価なファイル数です。
キー付き状態
「キー付き状態」という見出しのセクションNextPDF\Pro\Stream\State\KeyedStateStoreInterface
「NextPDF\Pro\Stream\State\KeyedStateStoreInterface」という見出しのセクションhas、get、put、remove、clear、加えてチェックポイント境界のための snapshot(): array と restore(array $snapshot): void。値は JSON シリアライズ可能でなければなりません。デフォルトの render-and-commit ワークロードではキー付き状態は使用されません。これは集約/ウィンドウ処理の拡張のために存在します。InMemoryKeyedStateStore は単一実行の実装です。復旧時にそれを失っても、デフォルトのワークロードではセマンティクス上の no-op です。なぜなら、exactly-once はコミッターのダイジェスト重複排除から来るからです。
NextPDF\Pro\Stream\State\KeySelector
「NextPDF\Pro\Stream\State\KeySelector」という見出しのセクションfinal readonly、__construct(string $tenantField = 'tenant_id', string $documentField = 'document_id')。keyFor(RenderManifest $manifest): non-empty-string は、マニフェストのメタデータからパーティションキーを rawurlencode(tenant):rawurlencode(document) として導出し(このエンコードが ("a:b","c") と ("a","b:c") の衝突を防ぎます)、いずれかのフィールドが欠けている場合はジョブ ID にフォールバックします — そのため、すべてのマニフェストは安定した空でないキーに解決されます。
永続性マーカーと例外
「永続性マーカーと例外」という見出しのセクションNextPDF\Pro\Stream\DurableCapability は、状態がプロセスの再起動を越えて存続する任意のストア/コミッターのためのマーカーインターフェースです。クラッシュに強い実行には、すべてのコラボレーターがこれを実装することが求められるため、インメモリストアが維持できない exactly-once を約束する代わりに、すばやく失敗します。
すべてのサブシステム例外は NextPDF\Pro\Stream\Exception\StreamException(Throwable を拡張)を実装するため、呼び出し元は catch (StreamException) で統一的に処理できます。
RenderEngineException(RuntimeException) — エグゼキューターがバッチの契約に違反した(不明、重複、または欠落したユニット、ワーカー障害、タイムアウト)。CommitIntegrityException(RuntimeException) — 宣言された sha-256 がペイロードと一致しない。spec コードSPEC-COMMIT-422。OutputCommitConflictException(RuntimeException) — overwrite を無効にした状態で、使用中のキーに異なるバイト列。spec コードSPEC-COMMIT-409(specCode()を介して公開)。UnsupportedTargetException(InvalidArgumentException) — コミッターが扱えないターゲットスキーム。
エンジンはマニフェストを Core のマニフェストモデルに対して検証し、決定論的なバイト列に sha-256 ダイジェストを加えたものを生成します。コミッターは、アトミックで整合性チェック済みの exactly-once 書き込みを強制します。このモジュールは、sha-256 のコンテンツダイジェストを超える暗号操作を一切実行せず、FIPS 固有の振る舞いを定義しません。
エッジケースと注意点
「エッジケースと注意点」という見出しのセクションrenderBatch()は、マニフェスト単位の失敗で中断することは決してありません。各EngineRenderResultを検査してください。ProcessPoolRenderUnitExecutorはインデックスによって厳格に相関付けし、ワーカーのバイト列を再ハッシュします。バグのあるワーカーは、出力を破損させるのではなくハードに失敗します。LocalFilesystemCommitterは単一ホストです。クロスホストの exactly-once には永続的な Enterprise コミッターが必要です。- クラッシュに強い実行は、インメモリのバリアントではなく、
DurableCapability(ファイルシステム)のストアを全体で使用しなければなりません。
公開の境界
「公開の境界」という見出しのセクションこのページは、外部から観測可能な振る舞いとサポートされている公開 API サーフェスのみを記述します。内部の名前空間パス、ヘルパークラス、メカニズムの表、runbook のファイル名、チケットのプレフィックスは対象外です。