Generazione di documenti ad alto volume
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
In breve
Sezione intitolata “In breve”Generare un PDF è una chiamata di funzione. Generarne centomila in base a una pianificazione è un problema di sistema: la memoria deve restare entro limiti controllati, il lavoro va parallelizzato e i numeri devono avere un significato. Questa pagina segue lo scenario di generazione in batch, dal problema del throughput fino a una distribuzione che regge il carico. Afferma chiaramente che la risposta onesta è «misuralo sui tuoi documenti», non una cifra a effetto.
Perché è importante
Sezione intitolata “Perché è importante”La generazione in batch fallisce in due modi tipici. Il primo è l’aumento progressivo della memoria. Un worker a lunga durata accumula stato trattenuto, documento dopo documento, finché non viene terminato a metà del batch, e l’esecuzione non risulta né completa né fallita in modo pulito. Il secondo è un numero presentato con sicurezza ma privo di significato: un benchmark ricavato da un documento banale viene usato per dimensionare una flotta che esegue il rendering di documenti complessi, e si rivela errato solo sotto il carico di produzione.
Si possono evitare entrambi, ma solo progettando fin dall’inizio il modello di memoria e il metodo di misurazione, anziché aggiungerli dopo il primo incidente.
In sintesi
Sezione intitolata “In sintesi”- L’unità di lavoro è un documento usa e getta, non condiviso. Mantenere i dati con durata pari a quella del processo (font, cache delle immagini) nei registri condivisi; creare e scartare il documento per ogni rendering.
- La memoria ha due parti, e solo una conta per un worker a lunga durata. Il picco transitorio durante un rendering è previsto; la memoria trattenuta che non viene rilasciata è la fuga che pone fine a un batch.
- Il throughput è parallelismo più un costo limitato per rendering. La forma che regge è una coda che alimenta worker stateless: ciascuno esegue il rendering e rilascia.
- Un numero senza il suo metodo non è un numero. NextPDF riporta le misurazioni per rendering come dati da raccogliere, e rifiuta affermazioni di velocità non qualificate. La cifra più importante è quella che si misura sui propri modelli (ISO 24495-1 §5.x11 — collocare il messaggio che conta dove il lettore lo trova).
Come NextPDF lo affronta
Sezione intitolata “Come NextPDF lo affronta”L’architettura è costruita attorno a una sola decisione: lo stato che vive per il processo è condiviso e immutabile; lo stato che vive per un rendering è nuovo e viene scartato. I font sono dati strutturali analizzati una sola volta e poi bloccati, così nessun rendering può modificarli e contaminare quello successivo. La cache delle immagini è un archivio limitato di tipo least-recently-used che non viene mai bloccato, così la memoria resta limitata senza fughe tra le richieste. La factory dei documenti è un singleton stateless; ogni documento creato è usa e getta.
È questa separazione a rendere sicura l’esecuzione di un worker per ore sotto Octane, RoadRunner o Swoole. Elimina per costruzione la modalità di errore in cui «la richiesta N corrompe la richiesta N+1», anziché sperare che il documento si reinizializzi da sé.
Lo scenario ha quattro fasi.
- 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.
I bridge dei framework rendono questa forma predefinita, anziché lasciarla come qualcosa da assemblare. Il service provider di Laravel registra il registro dei font come singleton preriscaldato e bloccato e registra il documento come istanza nuova a ogni resolve. Include un job in coda con un numero limitato di tentativi, un timeout e un backoff esponenziale. Il job convalida il proprio percorso di output sul lato worker, perché un payload di coda serializzato può essere manomesso durante il transito. Le integrazioni Symfony e CodeIgniter seguono la stessa disciplina: documento usa e getta e registro condiviso.
Cosa dicono le evidenze
Sezione intitolata “Cosa dicono le evidenze”Il modello di memoria è supportato dal codice. Evidence: Code-backed Il NextPdfServiceProvider di Laravel registra il FontRegistry come singleton preriscaldato e poi sottoposto a lock(), l’ImageRegistry come singleton limitato tramite LRU e deliberatamente non bloccato, e il Document come binding per-resolve tramite una factory stateless. Il modello di documento usa e getta è nel cablaggio, non nella prosa. Il GeneratePdfJob espone tries, timeout e backoff e riconvalida il proprio percorso di output all’interno di handle().
La superficie di misurazione è supportata da benchmark.
Evidence: Benchmark-backed Per ogni generazione, il motore emette un
RenderReport immutabile che riporta il tempo di rendering in millisecondi, il
picco di memoria in byte, il numero di pagine, i conteggi degli avvisi e le occorrenze di fallback — gli
input precisi necessari per dimensionare una flotta. Un analizzatore separato della frammentazione
della memoria distingue la memoria di picco (transitoria) da quella trattenuta. Questa
distinzione indica se un worker a lunga durata è sano o sta
lentamente perdendo memoria. Lo stesso harness di benchmark è configurato per esecuzioni
ripetute con riscaldamento, perché una singola misurazione del tempo è rumore.
La disciplina è un principio di progettazione: Evidence: Design principle NextPDF riporta le prestazioni insieme al metodo usato e rifiuta affermazioni di velocità non qualificate. Questo è coerente con il modo in cui è scritta questa documentazione — Spec: ISO 24495-1:2023, §5 ISO 24495-1:2023 §5 colloca il messaggio che conta dove il lettore lo troverà. Il messaggio che conta qui è «misura il tuo carico di lavoro».
Esempio pratico
Sezione intitolata “Esempio pratico”Il codice qui sotto è il ciclo con documento usa e getta e misurazione inclusa che illustra il modello. Il motore produce il RenderReport. La coda appartiene all’infrastruttura applicativa.
<?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), ]); }}L’unset() non è un dettaglio cosmetico. Lo stato specifico del rendering è pensato per essere rilasciato a ogni iterazione, così che la memoria trattenuta torni al valore di base. Un worker il cui valore di base cresce tra le iterazioni è l’errore che questo ciclo è progettato per evitare.
Errore comune
Sezione intitolata “Errore comune”L’errore principale è «quanti PDF al secondo può fare NextPDF?» come se avesse una sola risposta. Non esiste una risposta unica, e citarne una è il modo in cui le flotte vengono dimensionate male. Il costo del rendering è dominato dal documento, quindi l’unico numero su cui valga la pena agire è quello misurato sui propri modelli con il report per rendering prodotto dal motore. Una cifra priva del documento, dell’hardware e del metodo che la sostengono è decorazione, non dato.
Il secondo errore è ritenere che la memoria di picco sia l’aspetto da monitorare. Il picco è transitorio e previsto — viene rilasciato. Il numero che pone fine a un batch è la memoria trattenuta che non viene rilasciata. È esattamente per questo che il motore separa le due.
Limiti e confini
Sezione intitolata “Limiti e confini”- Non esiste una cifra di throughput universale, e questa pagina deliberatamente non ne indica nessuna. Il costo del rendering dipende dai propri documenti; misurare con il report per rendering.
- La memoria limitata dipende dall’uso del modello di documento usa e getta. Trattenere un documento attraverso molti rendering, o condividere stato mutabile per rendering, annulla la garanzia. I bridge dei framework adottano per impostazione predefinita la forma sicura. Il cablaggio fatto a mano deve replicarla.
- La cache delle immagini è limitata, non illimitata. Sotto carichi di lavoro intensi con immagini uniche, la LRU effettua espulsioni. È il comportamento previsto dal progetto, non una regressione.
- Il dimensionamento del pool di worker, la scelta della coda e l’autoscaling sono decisioni di distribuzione esterne al motore. NextPDF fornisce le misurazioni e la primitiva a memoria limitata. Non gestisce la coda.
RenderReportè un dato, non un verdetto. Indica cosa è accaduto in un rendering. Trasformarlo in un piano di capacità richiede un’analisi dedicata.- Questa pagina è supportata da benchmark per la superficie di misurazione e dal codice per il modello di memoria. Non afferma alcun valore specifico.
| Edition | Availability |
|---|---|
| Core | Il modello di documento usa e getta, i registri condivisi immutabili, il
|
| Pro | Stesse primitive; le funzionalità commerciali (firma, PDF/A) aggiungono un costo per rendering da misurare, non da presumere. |
| Enterprise | Stesse primitive; il lavoro di fattura strutturata e convalida aggiunge un ulteriore costo per rendering che scala con la dimensione del payload e del set di regole. |
Documentazione correlata
Sezione intitolata “Documentazione correlata”- Memoria e streaming — come il motore mantiene la memoria limitata su documenti di grandi dimensioni e dove ricorre allo streaming.
- Benchmarking onesto — che valore ha un numero di benchmark senza il suo metodo, e come NextPDF riporta le prestazioni.
- Gestione di NextPDF in produzione — trasformare i report per rendering in segnali di salute una volta che il batch è in esecuzione reale.
Glossario
Sezione intitolata “Glossario”- Documento usa e getta — un’istanza di documento creata per un singolo rendering e poi scartata, così che nessuno stato si propaghi al rendering successivo.
- Registro condiviso — stato con durata pari a quella del processo, immutabile dopo il preriscaldamento (font, cache delle immagini), riutilizzato tra i rendering senza costo per rendering.
- Memoria di picco — il livello massimo transitorio raggiunto durante un rendering; previsto e destinato a tornare al valore di base.
- Memoria trattenuta — memoria ancora occupata dopo il completamento di un rendering; un valore di base trattenuto che aumenta tra i rendering è una fuga.
- Worker — un processo a lunga durata che preleva job di rendering da una coda; deve mantenere la memoria entro limiti controllati per sopravvivere a un batch.
- RenderReport — l’istantanea immutabile delle metriche per rendering del motore (tempo, memoria di picco, numero di pagine, avvisi) usata per dimensionare la capacità a partire da dati reali.