Generowanie dokumentów na dużą skalę
Spec: ISO 24495-1:2023, §5 ISO 24495-1:2023 §5 Spec: ISO 9241-112:2025, §6.1.2.3 ISO 9241-112:2025 §6.1.2.3 Evidence: Benchmark-backed
W skrócie
Dział zatytułowany „W skrócie”Wygenerowanie jednego pliku PDF to jedno wywołanie funkcji. Wygenerowanie stu tysięcy zgodnie z harmonogramem jest już problemem systemowym: pamięć musi pozostać ograniczona, praca musi przebiegać równolegle, a liczby muszą coś znaczyć. Ta strona prowadzi przez scenariusz generowania wsadowego — od pytania o przepustowość po wdrożenie, które sprawdza się w praktyce. Mówi wprost, że uczciwa odpowiedź brzmi „zmierz to na własnych dokumentach”, a nie „efektowna liczba w nagłówku”.
Dlaczego to ma znaczenie
Dział zatytułowany „Dlaczego to ma znaczenie”Generowanie wsadowe zawodzi zwykle na dwa charakterystyczne sposoby. Pierwszy to stopniowe narastanie zużycia pamięci. Długo działający proces roboczy gromadzi stan utrzymywany między dokumentami, aż zostaje przerwany w połowie partii, a przebieg nie jest ani ukończony, ani jednoznacznie zakończony niepowodzeniem. Drugi to pozornie pewna, lecz bezwartościowa liczba: wynik testu na trywialnym dokumencie służy do oszacowania floty renderującej złożone dokumenty, a błąd wychodzi na jaw dopiero pod obciążeniem produkcyjnym.
Obu problemów można uniknąć, ale tylko wtedy, gdy strukturę pamięci i metodę pomiaru zaprojektuje się od początku, zamiast dokładać je po pierwszym incydencie.
W telegraficznym skrócie
Dział zatytułowany „W telegraficznym skrócie”- Jednostką pracy jest dokument jednorazowy, a nie współdzielony. Stan o czasie życia procesu (czcionki, pamięć podręczna obrazów) przechowuj we współdzielonych rejestrach; dokument twórz i odrzucaj przy każdym renderowaniu.
- Pamięć ma dwie części, a dla długo działającego procesu roboczego liczy się tylko jedna. Przejściowy szczyt podczas renderowania jest oczekiwany; utrzymywana pamięć, która nie jest zwalniana, to wyciek kończący partię.
- Przepustowość wynika ze zrównoleglenia i ograniczonego kosztu pojedynczego renderowania. Architekturą, która sprawdza się w praktyce, jest kolejka zasilająca bezstanowe procesy robocze, z których każdy renderuje i zwalnia zasoby.
- Liczba bez metody nie jest liczbą. NextPDF raportuje pomiary pojedynczego renderowania jako dane, które zbierasz, i odmawia formułowania nieuzasadnionych deklaracji o szybkości. Najważniejsza liczba to ta, którą zmierzysz na własnych szablonach (ISO 24495-1 §5.x11 — umieść istotną informację tam, gdzie czytelnik ją znajdzie).
Jak podchodzi do tego NextPDF
Dział zatytułowany „Jak podchodzi do tego NextPDF”Architektura opiera się na jednej decyzji: stan żyjący przez czas trwania procesu jest współdzielony i niezmienny; stan żyjący przez czas renderowania jest świeży i odrzucany. Czcionki są danymi strukturalnymi: analizuje się je raz, a następnie blokuje, dzięki czemu żadne renderowanie nie może ich zmienić ani zanieczyścić kolejnego. Pamięć podręczna obrazów to ograniczony magazyn typu least-recently-used, którego nigdy się nie blokuje, dzięki czemu pamięć pozostaje ograniczona i nie wycieka między żądaniami. Fabryka dokumentów to bezstanowy singleton; każdy tworzony przez nią dokument jest jednorazowy.
To właśnie ten podział sprawia, że proces roboczy można bezpiecznie uruchamiać przez wiele godzin pod Octane, RoadRunner lub Swoole. Eliminuje tryb awarii, w którym „żądanie N uszkadza żądanie N+1”, już na etapie konstrukcji, zamiast liczyć na to, że dokument sam się zresetuje.
Scenariusz składa się z czterech etapów.
- Warm the shared state once On worker boot, parse and lock the font registry and size the image cache. This cost is paid once, not per document.
- Enqueue the work A queue holds the render jobs. The queue is the throughput dial — workers scale horizontally behind it.
- Render on a disposable document Each worker creates a fresh document from the factory, renders, emits the bytes, and lets the document go.
- Measure, then size Collect per-render time and peak memory. Size the fleet from measurements on your own templates, not a generic figure.
Mostki integracyjne dla frameworków sprawiają, że ta architektura jest domyślna, a nie składana samodzielnie. Dostawca usług Laravel rejestruje rejestr czcionek jako rozgrzany, zablokowany singleton, a dokument wiąże jako świeżą instancję przy każdym rozwiązaniu zależności. Dostarcza zadanie kolejkowane z ograniczoną liczbą prób, limitem czasu i wykładniczym odstępem między ponowieniami. Zadanie weryfikuje swoją ścieżkę wyjściową po stronie procesu roboczego, ponieważ zserializowany ładunek z kolejki może zostać zmanipulowany w trakcie przesyłania. Integracje z Symfony i CodeIgniter stosują tę samą dyscyplinę dokumentu jednorazowego i współdzielonego rejestru.
Co mówią dowody
Dział zatytułowany „Co mówią dowody”Model pamięci jest poparty kodem. Evidence: Code-backed Laravelowy NextPdfServiceProvider rejestruje FontRegistry jako singleton, który jest rozgrzewany, a następnie blokowany metodą lock(), ImageRegistry jako singleton typu bounded-LRU, który celowo nie jest blokowany, oraz Document jako powiązanie tworzone przez bezstanową fabrykę przy każdym rozwiązaniu zależności. Model dokumentu jednorazowego wynika z okablowania, a nie tylko z opisu. GeneratePdfJob zawiera parametry tries, timeout i backoff oraz ponownie weryfikuje swoją ścieżkę wyjściową wewnątrz handle().
Warstwa pomiarowa jest poparta testami porównawczymi.
Evidence: Benchmark-backed Dla każdego generowania silnik emituje niezmienny raport
RenderReport, który zawiera czas renderowania w milisekundach, szczytowe
zużycie pamięci w bajtach, liczbę stron, liczbę ostrzeżeń oraz użycie rozwiązań awaryjnych — czyli
dokładnie te dane wejściowe, których potrzebujesz do wymiarowania floty. Osobny analizator fragmentacji
pamięci odróżnia pamięć szczytową (przejściową) od utrzymywanej. To
rozróżnienie pokazuje, czy długo działający proces roboczy jest sprawny, czy powoli
przecieka. Sam zestaw testów porównawczych jest skonfigurowany do powtarzanych
przebiegów z rozgrzewką, ponieważ pojedynczy pomiar czasu to szum.
Ta dyscyplina jest zasadą projektową: Evidence: Design principle NextPDF raportuje wydajność razem z metodą pomiaru i odmawia formułowania nieuzasadnionych deklaracji o szybkości. To spójne ze sposobem, w jaki napisano tę dokumentację — Spec: ISO 24495-1:2023, §5 ISO 24495-1:2023 §5 umieszcza istotną informację tam, gdzie czytelnik ją znajdzie. Istotną informacją jest tu „zmierz własne obciążenie”.
Praktyczny przykład
Dział zatytułowany „Praktyczny przykład”Poniższy kod pokazuje pętlę dokumentu jednorazowego z pomiarem. Silnik wytwarza RenderReport; kolejka należy do twojej infrastruktury.
<?php
declare(strict_types=1);
use NextPDF\Contracts\DocumentFactoryInterface;use NextPDF\Observability\RenderReport;use Psr\Log\LoggerInterface;
/** * One batch worker iteration: render, emit, release, measure. * * The factory and its registries are process-lifetime singletons; the * document is disposable. Retained memory must return to baseline between * iterations or the worker is leaking. * * @param iterable<int, callable(\NextPDF\Core\Document): \NextPDF\Core\Document> $jobs */function runBatch( DocumentFactoryInterface $factory, LoggerInterface $logger, iterable $jobs,): void { foreach ($jobs as $jobId => $build) { $startedAt = hrtime(true);
// Fresh, disposable document — shares the warmed registries. $doc = $factory->create(); $doc = $build($doc); $bytes = $doc->getPdfData();
// Hand the bytes off to your sink (object store, response, etc.). unset($doc, $bytes); // let the per-render state go
$elapsedMs = (hrtime(true) - $startedAt) / 1_000_000;
$logger->info('pdf.render.complete', [ 'job_id' => $jobId, 'render_time_ms' => round($elapsedMs, 2), 'peak_memory_mb' => round(memory_get_peak_usage(true) / 1_048_576, 2), ]); }}Wywołanie unset() nie jest kosmetyczne. Stan pojedynczego renderowania ma być zwalniany w każdej iteracji, aby utrzymywana pamięć wracała do poziomu bazowego. Proces roboczy, którego poziom bazowy rośnie w kolejnych iteracjach, to awaria, której ta pętla ma zapobiegać.
Częste nieporozumienie
Dział zatytułowany „Częste nieporozumienie”Najczęstsze nieporozumienie to pytanie „ile plików PDF na sekundę potrafi NextPDF?”, tak jakby istniała na nie jedna odpowiedź. Nie istnieje, a podawanie jej prowadzi do błędnego wymiarowania floty. Koszt renderowania zależy przede wszystkim od dokumentu, więc jedyną liczbą, na której warto się opierać, jest ta zmierzona na własnych szablonach z użyciem raportu pojedynczego renderowania z silnika. Liczba bez stojącego za nią dokumentu, sprzętu i metody jest ozdobą, a nie danymi.
Drugie nieporozumienie polega na tym, że to pamięć szczytową należy obserwować. Szczyt jest przejściowy i oczekiwany — wraca do normy. Liczbą, która kończy partię, jest utrzymywana pamięć, która nie wraca do normy. Właśnie dlatego silnik rozdziela te dwie wartości.
Ograniczenia i granice
Dział zatytułowany „Ograniczenia i granice”- Nie istnieje uniwersalna liczba przepustowości i ta strona celowo żadnej nie podaje. Koszt renderowania zależy od twoich dokumentów; zmierz go raportem pojedynczego renderowania.
- Ograniczone zużycie pamięci zależy od stosowania modelu dokumentu jednorazowego. Przetrzymywanie dokumentu przez wiele renderowań lub współdzielenie zmiennego stanu pojedynczego renderowania unieważnia tę gwarancję. Mostki integracyjne dla frameworków domyślnie przyjmują bezpieczną architekturę. Ręcznie wykonane okablowanie musi ją odtworzyć.
- Pamięć podręczna obrazów ma limit, nie jest nieograniczona. Przy dużym obciążeniu unikalnymi obrazami mechanizm LRU usuwa wpisy. Taki jest zamysł projektowy, a nie regres.
- Wymiarowanie puli procesów roboczych, wybór kolejki i automatyczne skalowanie to decyzje wdrożeniowe poza silnikiem. NextPDF dostarcza pomiary oraz ograniczone prymitywy. Nie zarządza twoją kolejką.
RenderReportto dane, a nie werdykt. Mówi, co się wydarzyło podczas renderowania. Przełożenie tego na plan wydajnościowy to już twoja analiza.- Ta strona jest poparta testami porównawczymi w zakresie warstwy pomiarowej oraz poparta kodem w zakresie modelu pamięci. Nie deklaruje żadnej konkretnej wartości.
| Edition | Availability |
|---|---|
| Core | Model dokumentu jednorazowego, współdzielone niezmienne rejestry,
|
| Pro | Te same prymitywy; funkcje komercyjne (podpisywanie, PDF/A) dodają koszt pojedynczego renderowania, który należy zmierzyć, a nie zakładać. |
| Enterprise | Te same prymitywy; obsługa faktur ustrukturyzowanych i walidacji dodaje dalszy koszt pojedynczego renderowania, który skaluje się wraz z rozmiarem ładunku i zestawu reguł. |
Powiązana dokumentacja
Dział zatytułowany „Powiązana dokumentacja”- Pamięć i strumieniowanie — jak silnik utrzymuje ograniczone zużycie pamięci przy dużych dokumentach i gdzie stosuje strumieniowanie.
- Uczciwe testy porównawcze — ile warta jest liczba z testu porównawczego bez jej metody i jak NextPDF raportuje wydajność.
- Eksploatacja NextPDF w środowisku produkcyjnym — przekładanie raportów pojedynczego renderowania na sygnały kondycji, gdy partia działa już produkcyjnie.
Słowniczek
Dział zatytułowany „Słowniczek”- Dokument jednorazowy — instancja dokumentu tworzona na potrzeby pojedynczego renderowania i odrzucana po nim, dzięki czemu żaden stan nie przecieka do kolejnego renderowania.
- Współdzielony rejestr — stan o czasie życia procesu, niezmienny po rozgrzaniu (czcionki, pamięć podręczna obrazów), używany ponownie w kolejnych renderowaniach bez ponoszenia kosztu przy każdym renderowaniu.
- Pamięć szczytowa — przejściowy najwyższy poziom zużycia podczas renderowania; oczekiwany i wracający do poziomu bazowego.
- Pamięć utrzymywana — pamięć nadal zajęta po zakończeniu renderowania; rosnący poziom bazowy utrzymywanej pamięci w kolejnych renderowaniach to wyciek.
- Proces roboczy — długo działający proces pobierający zadania renderowania z kolejki; musi utrzymywać ograniczone zużycie pamięci, aby przetrwać partię.
- RenderReport — niezmienna migawka metryk pojedynczego renderowania tworzona przez silnik (czas, pamięć szczytowa, liczba stron, ostrzeżenia), używana do wymiarowania wydajności na podstawie rzeczywistych danych.