コンテンツにスキップ
getnextpdf.com

Pro エディション

Stream — 詳細リファレンス

このページでは、概要ページの範囲を超えて、NextPDF\Pro\Stream サブシステムの公開コントラクト、クラス、メソッド、失敗モードを記述します。以下のすべての型は、文書化された Pro の公開サーフェスの一部です。

この機能は NextPDF Pronextpdf/pro)に同梱されており、Pro ティアのライセンスエンベロープで有効化されます。そのエンタイトルメントを持たないデプロイメントでは、この機能のクラスはロードされません。エディションを比較してライセンスを入手

機能ごとのライセンスフラグは適用されません。コードは Pro エディションに同梱されています。ワーカー数、バッチサイズ、リトライ予算、ストアバックエンドはランタイムのパラメーターです。

NextPDF\Pro\Stream\Engine\RenderEngineInterface は、スループットエンジンとドキュメントジョブのストリームプロセッサーの間のコントラクトです。エンジンがこれを実装し(並行性、ワーカープールのライフサイクル、バックプレッシャー、境界付きメモリを所有)、ストリームプロセッサーがこれを消費します(キー付き状態、重複排除、リトライ、チェックポイント、exactly-once コミットを所有)。エンジンはバイト列に sha-256 を加えたものを返し、コミット済みの場所は決して返しません — その副作用のなさこそが、プロセッサーがちょうど一度だけステージング、コミット、チェックポイントを行えるようにします。

public function renderBatch(array $manifests, array $variablesByJobId = []): array; // list<EngineRenderResult>, input order
public function maxBatchSize(): int; // int<1, max> backpressure hint
public function isAvailable(): bool;

$manifests は最大で maxBatchSize() のサイズの list<RenderManifest> です。$variablesByJobId は、ジョブ ID を array<string, scalar> のテンプレート変数に対応付けます。マニフェスト単位の失敗は項目ごとの Failed/Timeout の結果であり、バッチを中断させることは決してありません。

同期的・単一プロセスのベースライン。各マニフェストを 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 < 1InvalidArgumentException をスローします。isAvailable() は常に true です。

final readonly__construct(RenderUnitExecutorInterface $executor)。各マニフェストをインデックス付きの RenderUnit でラップし、それらをエグゼキューターを通じて実行し、完了をインデックスで再ソートして、出力がシーケンシャルなレンダリングとバイト単位で同一になるようにします。[0, count) の外側にある完了インデックスは RenderEngineException::unknownUnit() をスローし、重複したインデックスは duplicateResult() を、欠落したインデックスは missingResult() をスローします。maxBatchSize()isAvailable() はエグゼキューターに委譲します。

public function execute(array $units): iterable; // iterable<CompletedRenderUnit>, any order
public function maxBatchSize(): int;
public function isAvailable(): bool;

実装は完了を任意の順序で yield してよく、ConcurrentRenderEngine がインデックスで順序を復元します。

final readonly__construct(RenderEngineInterface $inner)。各ユニットを内部エンジンを通じて順番にレンダリングします — 並列エグゼキューターがバイト単位で一致しなければならない、決定論的な正しさのリファレンスです。時間、プロセス、スレッド、ランダム性はありません。

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 をスローします。

final readonlyint<0, max> $indexRenderManifest $manifestarray<string, scalar> $variables。相関付けは index によって行われ、ジョブ ID によっては決して行われません(ジョブ ID はバッチ内で一意であることが保証されません)。

final readonlyint $index(信頼できない、エンジンが検証)、EngineRenderResult $result

final readonly。フィールド: jobIdEngineRenderStatus $status?string $bytes?string $sha256int $pageCount?string $errorCode?string $errorMessagearray<non-empty-string, float> $timings。ファクトリー: rendered(jobId, bytes, sha256, pageCount, timings = [])failed(jobId, errorCode, errorMessage)timedOut(jobId, errorMessage)(コード SPEC-ENGINE-TIMEOUT)。isRendered() はステータスを報告します。レンダリングされた結果は、バイト列とダイジェストを運び、コミット済みの場所は決して運びません。

文字列で裏付けられた enum: RenderedFailedTimeoutisRetryable()Timeout に対してのみ true であるため、呼び出し元はエラーを再検査せずにタイムアウトを一時的なものとして分類します。

public function commit(
string $jobId,
OutputObjectKey $target,
string $bytes,
string $sha256,
bool $overwrite = false,
): CommitReceipt;

Exactly-once の公開: アトミック、冪等(バイト単位で同一の再コミットは書き込みを行わず、idempotentReuse = true を持つ CommitReceipt を返します — 元のものではなく、新しいレシートです。その committedAt は現在のクロックです)、サイレントな上書きなし、整合性チェック済み(コミッターがダイジェストを再計算)です。失敗モード: CommitIntegrityException(宣言された sha-256 がバイト列と一致しない)、OutputCommitConflictExceptionoverwrite = false で使用中のキーに異なるバイト列)、UnsupportedTargetException(サポートされていないターゲットスキーム)。

final readonlyOutputCommitterInterface, DurableCapability を実装します。__construct(string $rootDirectory, ?AtomicFileWriter $writer = null, ?ClockInterface $clock = null)file スキームのみを扱います。すべてのターゲットを 1 つの構成済みルートの下で解決し、アトミックライター(O_EXCL temp → fsync → 同一ボリュームでのリネーム)を通じて書き込みます。クリティカルセクション全体(親ディレクトリの作成を含む)は、出力キースペースの外側に保持されたルートごとのロックファイルに対する排他的な flock の下で実行され、ロックを開けないか取得できない場合、コミットはフェイルクローズドです。シンボリックリンクされた最終コンポーネントと、コロンを含むあらゆるキー(NTFS の代替データストリームベクター)を拒否します。同一キーに対するクロスホストの並行 exactly-once には、永続的な Enterprise コミッターが必要です。システムの一時ディレクトリであるか、それを含むルートは InvalidArgumentException をスローします。

final readonlyjobIdOutputObjectKey $targetsha256int<0, max> $bytesWrittenbool $idempotentReuseDateTimeImmutable $committedAttoArray() / fromArray() は完全にラウンドトリップ可能です(ターゲットは構造化されており、損失を伴う URI ではありません)。fromArray() は厳格であり、欠落または不正なフィールドに対して InvalidArgumentException をスローします。

load(string $runId): ?RunCheckpointsave(RunCheckpoint $checkpoint): void(永続的かつアトミック — リーダーは書き込み途中のチェックポイントを決して見ません)。

final readonlyrunIdint<0, max> $committedOffsetarray $keyedStateDateTimeImmutable $updatedAtSCHEMA_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 はコミッターのダイジェスト重複排除から来るからです。

final readonlyCheckpointStoreInterface, DurableCapability を実装します。実行ごとに 1 つの JSON ファイルをアトミックに書き込みます。run id は [A-Za-z0-9._-]+ に一致し、.. を含んではなりません。存在しないディレクトリは InvalidArgumentException をスローします。

isCommitted(IdempotencyKey $key): boolmarkCommitted(IdempotencyKey $key, CommitReceipt $receipt): voidreceiptFor(IdempotencyKey $key): ?CommitReceipt。リプレイをレンダリングする前にショートカットするファストパスです。コミッターのダイジェスト比較が永続的な保証であり続けるため、レコードが失われても、最悪の場合に発生するのはコミッターが重複排除する無駄な再レンダリングだけです。

  • InMemoryIdempotencyStore — 単一実行/テストスコープ(クラッシュ時に失われます)。
  • FilesystemIdempotencyStoreDurableCapability。コミット済みキーごとに 1 つのアトミックな JSON ファイル(シリアライズされたレシート)を、キー値のハッシュで命名します。マークは冪等であり、並行する再マークは 1 つのファイル上で無害に競合します。存在しないディレクトリは InvalidArgumentException をスローします。

final readonlypositive-int $maxAttemptspositive-int $baseDelayMspositive-int $maxDelayMs__construct(int $maxAttempts = 3, int $baseDelayMs = 100, int $maxDelayMs = 30000)。不変条件は maxAttempts >= 11 <= baseDelayMs <= maxDelayMs <= 7 days(さもなければ InvalidArgumentException)です。ファクトリー default()none()(単一試行)。shouldRetry(int $attempt): booldelayMsForAttempt(int $attempt): int<0, max> は、maxDelayMs で上限を設けた決定論的な指数バックオフ baseDelayMs * 2^(attempt-1) です(組み込みのジッターはありません。呼び出し側で適用してください)。

add(DeadLetterRecord $record): voidall(): list<DeadLetterRecord>count(): int<0, max>

final readonlyjobIdidempotencyKeyValuepositive-int $attemptslastErrorCodelastErrorMessageDateTimeImmutable $failedAt、オプションの ?string $runId、オプションの int<1, max> $sourceOffsetdedupKey() は、両方が分かっている場合は runId:sourceOffset であり、そうでなければ冪等性キー値です。fromArray()failed_at を ATOM として厳格に解析する(相対表現や非 ATOM 表現を拒否する)ため、シリアライズ/デシリアライズが対称に保たれます。

  • InMemoryDeadLetterStore — 単一実行/テストスコープ。
  • FilesystemDeadLetterStoreDurableCapability。レコードごとに 1 つのアトミックな JSON ファイルを、重複排除キーの SHA-256 ハッシュ(….dlq.json)で命名するため、再開時に同じ項目を再追加しても冪等です。all() はレコードを決定論的な(ソートされた)順序で読み取り、破損したレコードをスローによって露出させます。count() は妥当性チェックではなく安価なファイル数です。

hasgetputremoveclear、加えてチェックポイント境界のための snapshot(): arrayrestore(array $snapshot): void。値は JSON シリアライズ可能でなければなりません。デフォルトの render-and-commit ワークロードではキー付き状態は使用されません。これは集約/ウィンドウ処理の拡張のために存在します。InMemoryKeyedStateStore は単一実行の実装です。復旧時にそれを失っても、デフォルトのワークロードではセマンティクス上の no-op です。なぜなら、exactly-once はコミッターのダイジェスト重複排除から来るからです。

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\StreamExceptionThrowable を拡張)を実装するため、呼び出し元は catch (StreamException) で統一的に処理できます。

  • RenderEngineExceptionRuntimeException) — エグゼキューターがバッチの契約に違反した(不明、重複、または欠落したユニット、ワーカー障害、タイムアウト)。
  • CommitIntegrityExceptionRuntimeException) — 宣言された sha-256 がペイロードと一致しない。spec コード SPEC-COMMIT-422
  • OutputCommitConflictExceptionRuntimeException) — overwrite を無効にした状態で、使用中のキーに異なるバイト列。spec コード SPEC-COMMIT-409specCode() を介して公開)。
  • UnsupportedTargetExceptionInvalidArgumentException) — コミッターが扱えないターゲットスキーム。

エンジンはマニフェストを Core のマニフェストモデルに対して検証し、決定論的なバイト列に sha-256 ダイジェストを加えたものを生成します。コミッターは、アトミックで整合性チェック済みの exactly-once 書き込みを強制します。このモジュールは、sha-256 のコンテンツダイジェストを超える暗号操作を一切実行せず、FIPS 固有の振る舞いを定義しません。

  • renderBatch() は、マニフェスト単位の失敗で中断することは決してありません。各 EngineRenderResult を検査してください。
  • ProcessPoolRenderUnitExecutor はインデックスによって厳格に相関付けし、ワーカーのバイト列を再ハッシュします。バグのあるワーカーは、出力を破損させるのではなくハードに失敗します。
  • LocalFilesystemCommitter は単一ホストです。クロスホストの exactly-once には永続的な Enterprise コミッターが必要です。
  • クラッシュに強い実行は、インメモリのバリアントではなく、DurableCapability(ファイルシステム)のストアを全体で使用しなければなりません。

このページは、外部から観測可能な振る舞いとサポートされている公開 API サーフェスのみを記述します。内部の名前空間パス、ヘルパークラス、メカニズムの表、runbook のファイル名、チケットのプレフィックスは対象外です。