Przejdź do głównej zawartości
getnextpdf.com

Pro edycja

Stream — pełna dokumentacja referencyjna

Ta strona dokumentuje publiczne kontrakty, klasy, metody i tryby awarii podsystemu NextPDF\Pro\Stream wykraczające poza stronę przeglądową. Każdy poniższy typ jest częścią udokumentowanej publicznej powierzchni Pro.

Ta funkcja jest dostarczana w NextPDF Pro (nextpdf/pro) i aktywuje się wraz z kopertą licencyjną poziomu Pro. Wdrożenie bez tego uprawnienia nie ładuje klas tej funkcji. Porównaj edycje i uzyskaj licencję.

Nie obowiązuje żadna flaga licencji na poziomie pojedynczej funkcji; kod jest dostarczany z edycją Pro. Liczba pracowników, rozmiar partii, budżet ponowień oraz backend magazynu to parametry czasu działania.

NextPDF\Pro\Stream\Engine\RenderEngineInterface to kontrakt między silnikiem przepustowości a procesorem strumienia zadań dokumentowych. Silnik go implementuje (zarządzając współbieżnością, cyklem życia puli pracowników, przeciwciśnieniem (backpressure), ograniczoną pamięcią); procesor strumienia go konsumuje (zarządzając stanem z kluczami, deduplikacją, ponawianiem, punktami kontrolnymi i zatwierdzaniem dokładnie raz). Silnik zwraca bajty wraz z sha-256, nigdy zatwierdzonej lokalizacji — to właśnie ta wolność od skutków ubocznych pozwala procesorowi przygotować, zatwierdzić i utworzyć punkt kontrolny dokładnie raz.

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 to list<RenderManifest> o rozmiarze co najwyżej maxBatchSize(); $variablesByJobId mapuje identyfikator zadania na zmienne szablonu array<string, scalar>. Niepowodzenie pojedynczego manifestu to wynik Failed/Timeout dla danego elementu i nigdy nie przerywa partii.

Synchroniczny, jednoprocesowy punkt odniesienia. Waliduje każdy manifest w trybie fail-closed za pomocą RenderManifestValidator (limit 16 MiB ładunku inline, listy dozwolonych dla zgodności/podpisu, format skrótu treści sha-256, składnia lokalizacji BCP-47) przed renderowaniem przez SingleDocumentRenderer z Core. Blokujący błąd walidacji powoduje zwarcie do EngineRenderResult::failed(jobId, 'SPEC-MANIFEST-INVALID', ...); wyjątek renderowania staje się 'SPEC-RENDER-EXCEPTION'. Konstruktor: __construct(SingleDocumentRenderer $renderer, int $maxBatchSize = 64, ?RenderManifestValidator $validator = null)maxBatchSize < 1 zgłasza InvalidArgumentException. isAvailable() jest zawsze true.

final readonly, __construct(RenderUnitExecutorInterface $executor). Opakowuje każdy manifest w indeksowany RenderUnit, uruchamia je przez wykonawcę i ponownie sortuje ukończenia według indeksu, więc wynik jest bajtowo identyczny z renderowaniem sekwencyjnym. Indeks ukończenia spoza [0, count) zgłasza RenderEngineException::unknownUnit(); powtórzony indeks zgłasza duplicateResult(); brakujący indeks zgłasza missingResult(). maxBatchSize() i isAvailable() delegują do wykonawcy.

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

Implementacje mogą zwracać ukończenia w dowolnej kolejności; ConcurrentRenderEngine odtwarza kolejność według indeksu.

final readonly, __construct(RenderEngineInterface $inner). Renderuje każdą jednostkę po kolei przez wewnętrzny silnik — deterministyczne odniesienie poprawności, które równoległy wykonawca musi dopasować bajt w bajt. Bez czasu, procesów, wątków ani losowości.

NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor

Dział zatytułowany „NextPDF\Pro\Stream\Engine\ProcessPoolRenderUnitExecutor”

final readonly. Rozdziela partię na maksymalnie maxWorkers podprocesów roboczych php (po jednym fragmencie każdy), które renderują równolegle; wynik jest bajtowo identyczny z punktem odniesienia 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
)

Kontrakt odporności:

  • Wolny od zakleszczeń, bezpieczny dla Windows. Ładunki jednostek i wyniki podróżują przez pliki tymczasowe, a nie potoki; rodzic odpytuje proc_get_status() i opróżnia potok do EOF dopiero po zakończeniu pracownika, więc pracownik nie może zaklinować rodzica.
  • Ograniczone oczekiwanie. timeoutSeconds ogranicza całe renderowanie równoległe; po wygaśnięciu każdy wciąż działający pracownik jest terminowany i zgłaszany jest RenderEngineException.
  • Higiena zasobów. Blok finally zamyka potoki, podejmuje ograniczoną próbę terminowania i odzyskania (reap) ocalałych pracowników (łagodne terminowanie → wymuszone zabicie → odzyskanie; dziecko, którego nie zaobserwowano jako zatrzymane w ograniczonym czasie łaski, jest porzucane zamiast ryzykować nieokreślone zablokowanie) oraz odlinkowuje każdy plik tymczasowy na wszystkich ścieżkach.
  • Zaufana korelacja. Każdy pracownik musi zwrócić dokładnie przypisany mu zestaw indeksów (bez brakującego, zduplikowanego ani obcego indeksu); bajty każdego wyrenderowanego wyniku są ponownie hashowane i porównywane z sha-256 zgłoszonym przez pracownika, a każdy status inny niż rendered/failed twardo zawodzi. Niepowodzenie renderowania pojedynczego manifestu to wynik Failed dla danej jednostki; tylko usterka infrastrukturalna (niezerowe wyjście, nieczytelny/uszkodzony wynik, timeout) twardo zawodzi wykonawcę.

isAvailable() wymaga istnienia zarówno pliku autoload, jak i skryptu pracownika. Niedodatnie ograniczenie lub ujemny timeout zgłasza InvalidArgumentException.

final readonlyint<0, max> $index, RenderManifest $manifest, array<string, scalar> $variables. Korelacja odbywa się po index, nigdy po identyfikatorze zadania (identyfikatory zadań nie są gwarantowanie unikalne w obrębie partii).

final readonlyint $index (niezaufany, walidowany przez silnik), EngineRenderResult $result.

final readonly. Pola: jobId, EngineRenderStatus $status, ?string $bytes, ?string $sha256, int $pageCount, ?string $errorCode, ?string $errorMessage, array<non-empty-string, float> $timings. Fabryki: rendered(jobId, bytes, sha256, pageCount, timings = []), failed(jobId, errorCode, errorMessage), timedOut(jobId, errorMessage) (kod SPEC-ENGINE-TIMEOUT). isRendered() raportuje status. Wyrenderowany wynik niesie bajty i skrót, nigdy zatwierdzonej lokalizacji.

Wyliczenie oparte na łańcuchu: Rendered, Failed, Timeout. isRetryable() jest true tylko dla Timeout, więc wywołujący klasyfikuje timeout jako przejściowy bez ponownego badania błędu.

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

Publikacja dokładnie raz: atomowa, idempotentna (bajtowo identyczne ponowne zatwierdzenie nie wykonuje żadnego zapisu i zwraca CommitReceipt z idempotentReuse = true — nowe potwierdzenie, a nie oryginalne; jego committedAt to bieżący zegar), bez cichego nadpisywania oraz ze sprawdzeniem integralności (committer przelicza skrót). Tryby awarii: CommitIntegrityException (zadeklarowane sha-256 nie pasuje do bajtów), OutputCommitConflictException (rozbieżne bajty przy zajętym kluczu z overwrite = false), UnsupportedTargetException (nieobsługiwany schemat celu).

final readonly, implementuje OutputCommitterInterface, DurableCapability. __construct(string $rootDirectory, ?AtomicFileWriter $writer = null, ?ClockInterface $clock = null). Obsługuje wyłącznie schemat file; rozwiązuje każdy cel pod jednym skonfigurowanym katalogiem głównym i zapisuje przez atomowy pisarz (O_EXCL temp → fsync → zmiana nazwy w obrębie tego samego woluminu). Cała sekcja krytyczna (w tym tworzenie katalogu nadrzędnego) działa pod wyłącznym flock na pliku blokady dla danego katalogu głównego, trzymanym poza przestrzenią kluczy wyjścia, a zatwierdzenie jest fail-closed, jeśli blokady nie da się otworzyć ani uzyskać. Odmawia obsługi ostatecznych komponentów będących dowiązaniami symbolicznymi oraz każdego klucza zawierającego dwukropek (wektor alternatywnego strumienia danych NTFS). Międzyhostowe dostarczanie dokładnie raz do tego samego klucza współbieżnie wymaga trwałego committera Enterprise. Katalog główny, który jest systemowym katalogiem tymczasowym lub go zawiera, zgłasza InvalidArgumentException.

final readonlyjobId, OutputObjectKey $target, sha256, int<0, max> $bytesWritten, bool $idempotentReuse, DateTimeImmutable $committedAt. toArray() / fromArray() są w pełni odwracalne (cel jest ustrukturyzowany, a nie stratnym URI); fromArray() jest ścisłe i zgłasza InvalidArgumentException przy brakujących lub zniekształconych polach.

NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface

Dział zatytułowany „NextPDF\Pro\Stream\Checkpoint\CheckpointStoreInterface”

load(string $runId): ?RunCheckpoint oraz save(RunCheckpoint $checkpoint): void (trwałe i atomowe — czytelnik nigdy nie widzi połowicznie zapisanego punktu kontrolnego).

final readonlyrunId, int<0, max> $committedOffset, array $keyedState, DateTimeImmutable $updatedAt; SCHEMA_VERSION = '1.0'. Fabryki start(runId, at) oraz advancedTo(committedOffset, keyedState, at). toArray()/toJson()/fromArray()/fromJson() serializują go; fromArray() wymaga niepustego identyfikatora przebiegu i prawidłowego updated_at, odrzuca niezgodne (nie-1.x) schema_version oraz normalizuje stan z kluczami, odrzucając na każdej głębokości wszelkie wartości nieserializowalne do JSON, tak aby odtworzony stan zawsze dało się ponownie zserializować. Przy odtwarzaniu procesor przewija do przodu poza committedOffset i odtwarza stan z kluczami; stan zmutowany po ostatniej barierze jest przeliczany do przodu, nigdy nie jest błędem, ponieważ trwałe dokładnie raz pochodzi z deduplikacji skrótów w committerze.

NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore

Dział zatytułowany „NextPDF\Pro\Stream\Checkpoint\FilesystemCheckpointStore”

final readonly, implementuje CheckpointStoreInterface, DurableCapability. Jeden plik JSON na przebieg, zapisywany atomowo. Identyfikatory przebiegów muszą pasować do [A-Za-z0-9._-]+ i nie mogą zawierać ..; nieistniejący katalog zgłasza InvalidArgumentException.

isCommitted(IdempotencyKey $key): bool, markCommitted(IdempotencyKey $key, CommitReceipt $receipt): void, receiptFor(IdempotencyKey $key): ?CommitReceipt. Szybka ścieżka, która zwarciowo pomija renderowanie powtórki; porównanie skrótów w committerze pozostaje trwałą gwarancją, więc utracony rekord w najgorszym razie marnuje ponowne renderowanie, które committer zdeduplikuje.

  • InMemoryIdempotencyStore — zakres pojedynczego przebiegu / testu (tracony przy awarii).
  • FilesystemIdempotencyStoreDurableCapability; jeden atomowy plik JSON na zatwierdzony klucz (zserializowane potwierdzenie), nazwany skrótem wartości klucza. Oznaczenia są idempotentne; współbieżne ponowne oznaczenie ściga się nieszkodliwie na jednym pliku. Nieistniejący katalog zgłasza InvalidArgumentException.

final readonlypositive-int $maxAttempts, positive-int $baseDelayMs, positive-int $maxDelayMs. __construct(int $maxAttempts = 3, int $baseDelayMs = 100, int $maxDelayMs = 30000) z niezmiennikami maxAttempts >= 1 oraz 1 <= baseDelayMs <= maxDelayMs <= 7 days (w przeciwnym razie InvalidArgumentException). Fabryki default() oraz none() (pojedyncza próba). shouldRetry(int $attempt): bool. delayMsForAttempt(int $attempt): int<0, max> to deterministyczne wykładnicze odczekiwanie baseDelayMs * 2^(attempt-1) ograniczone do maxDelayMs (bez wbudowanego jittera; zastosuj go w miejscu wywołania).

add(DeadLetterRecord $record): void, all(): list<DeadLetterRecord>, count(): int<0, max>.

final readonlyjobId, idempotencyKeyValue, positive-int $attempts, lastErrorCode, lastErrorMessage, DateTimeImmutable $failedAt, opcjonalne ?string $runId, opcjonalne int<1, max> $sourceOffset. dedupKey() to runId:sourceOffset, gdy oba są znane, w przeciwnym razie wartość klucza idempotentności. fromArray() parsuje failed_at ściśle jako ATOM (odrzucając wyrażenia względne lub nie-ATOM), więc serializacja/deserializacja pozostaje symetryczna.

  • InMemoryDeadLetterStore — zakres pojedynczego przebiegu / testu.
  • FilesystemDeadLetterStoreDurableCapability; jeden atomowy plik JSON na rekord, nazwany skrótem SHA-256 klucza deduplikacji (….dlq.json), więc ponowne dodanie tego samego elementu przy wznowieniu jest idempotentne. all() odczytuje rekordy w deterministycznej (posortowanej) kolejności i ujawnia uszkodzony rekord przez zgłoszenie wyjątku; count() to tani zliczanie plików, a nie kontrola poprawności.

has, get, put, remove, clear, a także snapshot(): array oraz restore(array $snapshot): void dla granicy punktu kontrolnego. Wartości muszą być serializowalne do JSON. Dla domyślnego obciążenia render-and-commit żaden stan z kluczami nie jest używany; istnieje on dla rozszerzeń agregacji/okienkowania. InMemoryKeyedStateStore to implementacja pojedynczego przebiegu; jego utrata przy odtwarzaniu jest semantycznie bezskuteczna dla domyślnego obciążenia, ponieważ dokładnie raz pochodzi z deduplikacji skrótów w committerze.

final readonly, __construct(string $tenantField = 'tenant_id', string $documentField = 'document_id'). keyFor(RenderManifest $manifest): non-empty-string wyprowadza klucz partycji z metadanych manifestu jako rawurlencode(tenant):rawurlencode(document) (kodowanie powstrzymuje kolizję ("a:b","c") z ("a","b:c")), z fallbackiem do identyfikatora zadania, gdy któregokolwiek pola brakuje — więc każdy manifest rozwiązuje się do stabilnego, niepustego klucza.

NextPDF\Pro\Stream\DurableCapability to interfejs znacznikowy dla każdego magazynu/committera, którego stan przetrwa restart procesu. Przebieg odporny na awarie wymaga, aby każdy współpracownik go implementował, więc zawodzi szybko, zamiast obiecywać dokładnie raz, którego magazyn w pamięci nie jest w stanie utrzymać.

Wszystkie wyjątki podsystemu implementują NextPDF\Pro\Stream\Exception\StreamException (rozszerza Throwable), więc wywołujący może jednolicie catch (StreamException):

  • RenderEngineException (RuntimeException) — wykonawca naruszył kontrakt partii (nieznana, zduplikowana lub brakująca jednostka; usterka pracownika; timeout).
  • CommitIntegrityException (RuntimeException) — zadeklarowane sha-256 nie pasuje do ładunku; kod spec SPEC-COMMIT-422.
  • OutputCommitConflictException (RuntimeException) — rozbieżne bajty przy zajętym kluczu z wyłączonym nadpisywaniem; kod spec SPEC-COMMIT-409 (udostępniany przez specCode()).
  • UnsupportedTargetException (InvalidArgumentException) — schemat celu, którego committer nie potrafi obsłużyć.

Silnik waliduje manifesty względem modelu manifestu Core i produkuje deterministyczne bajty wraz ze skrótami sha-256; committer egzekwuje atomowe, sprawdzane pod kątem integralności zapisy dokładnie raz. Moduł nie wykonuje żadnych operacji kryptograficznych poza skrótami treści sha-256 i nie definiuje zachowania specyficznego dla FIPS.

  • renderBatch() nigdy nie przerywa przy niepowodzeniu pojedynczego manifestu; sprawdź każdy EngineRenderResult.
  • ProcessPoolRenderUnitExecutor koreluje ściśle po indeksie i ponownie hashuje bajty pracownika; wadliwy pracownik twardo zawodzi, zamiast zepsuć wynik.
  • LocalFilesystemCommitter jest jednohostowy; międzyhostowe dokładnie raz wymaga trwałego committera Enterprise.
  • Przebiegi odporne na awarie muszą wszędzie używać magazynów DurableCapability (na systemie plików), a nie wariantów w pamięci.

Ta strona dokumentuje wyłącznie zachowanie obserwowalne z zewnątrz oraz wspieraną publiczną powierzchnię API. Wewnętrzne ścieżki przestrzeni nazw, klasy pomocnicze, tabele mechanizmów, nazwy plików runbooków i prefiksy zgłoszeń są poza zakresem.