Geração de documentos em 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
Visão geral
Seção intitulada “Visão geral”Gerar um PDF é uma chamada de função. Gerar cem mil de forma agendada é um problema de sistemas: memória que precisa permanecer limitada, trabalho que precisa ser paralelo e números que precisam significar algo. Esta página percorre o cenário de geração em lote, do problema de throughput até uma implantação que se sustenta. Ela afirma claramente que a resposta honesta é “meça nos seus documentos”, e não um número chamativo.
Por que isso importa
Seção intitulada “Por que isso importa”A geração em lote falha de duas formas típicas. A primeira é o crescimento gradual da memória. Um worker de vida longa acumula estado retido, documento após documento, até ser encerrado no meio do lote, e a execução não termina nem falha de forma limpa. A segunda é um número confiante, porém sem sentido: um benchmark de um documento trivial é usado para dimensionar uma frota que renderiza documentos complexos, e o erro só fica evidente sob carga de produção.
Você pode evitar os dois problemas, mas apenas se projetar o modelo de memória e o método de medição desde o início, em vez de adicioná-los depois do primeiro incidente.
A versão curta
Seção intitulada “A versão curta”- A unidade de trabalho é um documento descartável, não um documento compartilhado. Mantenha os dados de duração do processo (fontes, cache de imagens) em registries compartilhados; crie e descarte o documento a cada renderização.
- A memória tem duas partes, e apenas uma importa para um worker de vida longa. O pico transitório durante uma renderização é esperado; a memória retida que não volta é o vazamento que encerra um lote.
- O throughput é paralelismo somado a um custo limitado por renderização. O formato que se sustenta é uma fila alimentando workers stateless, cada um renderizando e liberando.
- Um número sem o seu método não é um número. O NextPDF reporta medições por renderização como dados que você coleta, e recusa alegações de velocidade não qualificadas. O número mais importante é aquele que você mede nos seus próprios templates (ISO 24495-1 §5.x11 — coloque a mensagem que importa onde o leitor vai encontrá-la).
Como o NextPDF aborda isso
Seção intitulada “Como o NextPDF aborda isso”A arquitetura é construída em torno de uma única decisão: o estado que vive durante o processo é compartilhado e imutável; o estado que vive durante uma renderização é novo e descartado. As fontes são dados estruturais analisados uma vez e então bloqueados, de modo que nenhuma renderização possa alterá-los e poluir a próxima. O cache de imagens é um armazenamento limitado, do tipo least-recently-used, que nunca é bloqueado, de modo que a memória permanece limitada sem vazar entre requisições. A factory de documentos é um singleton stateless; todo documento que ela cria é descartável.
Essa separação é o que torna seguro executar um worker por horas sob Octane, RoadRunner ou Swoole. Ela elimina por construção o modo de falha em que “a requisição N corrompe a requisição N+1”, em vez de torcer para que o documento se reinicialize sozinho.
O cenário tem quatro estágios.
- 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.
As bridges de framework tornam esse formato o padrão, em vez de algo que você precisa montar. O service provider do Laravel registra o registry de fontes como um singleton aquecido e bloqueado, e vincula o documento como uma nova instância a cada resolução. Ele inclui um job enfileirado com tentativas limitadas, um timeout e backoff exponencial. Esse job valida o caminho de saída no lado do worker, porque um payload de fila serializado pode ser adulterado em trânsito. As integrações com Symfony e CodeIgniter seguem a mesma disciplina de documento descartável e registry compartilhado.
O que a evidência diz
Seção intitulada “O que a evidência diz”O modelo de memória é respaldado por código. Evidence: Code-backed O NextPdfServiceProvider do Laravel registra o FontRegistry como um singleton que é aquecido e então bloqueado com lock(), o ImageRegistry como um singleton bounded-LRU que deliberadamente não é bloqueado, e o Document como um binding por resolução via uma factory stateless. O modelo de documento descartável está no wiring, não na prosa. O GeneratePdfJob carrega tries, timeout e backoff e revalida o caminho de saída dentro de handle().
A superfície de medição é respaldada por benchmark.
Evidence: Benchmark-backed O motor emite um
RenderReport imutável por geração, carregando o tempo de renderização em milissegundos, pico de
memória em bytes, contagem de páginas, contagens de avisos e ocorrências de fallback — as
dados exatos de que você precisa para dimensionar uma frota. Um analisador separado de fragmentação de
memória distingue a memória de pico (transitória) da memória retida. Essa
distinção indica se um worker de vida longa está saudável ou vazando
lentamente. O próprio harness de benchmark é configurado para iterações
repetidas com aquecimento, porque uma única medição de tempo é só ruído.
A disciplina é um princípio de design: Evidence: Design principle o NextPDF reporta desempenho junto com o seu método e recusa alegações de velocidade não qualificadas. Isso é consistente com a forma como esta documentação é escrita — Spec: ISO 24495-1:2023, §5 ISO 24495-1:2023 §5 coloca a mensagem que importa em um ponto onde o leitor a encontrará. A mensagem que importa aqui é “meça a sua própria carga de trabalho”.
Exemplo prático
Seção intitulada “Exemplo prático”O código abaixo é o loop com documento descartável e medição. O motor produz o RenderReport; a fila fica por conta da sua infraestrutura.
<?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), ]); }}O unset() não é cosmético. O estado por renderização deve ser liberado a cada iteração para que a memória retida retorne ao baseline. Um worker cujo baseline sobe ao longo das iterações apresenta exatamente a falha que este loop foi projetado para evitar.
Equívoco comum
Seção intitulada “Equívoco comum”O equívoco principal é “quantos PDFs por segundo o NextPDF consegue fazer?” como se houvesse uma única resposta. Não há, e citar uma resposta é como as frotas acabam mal dimensionadas. O custo de renderização é dominado pelo documento, então o único número que vale a pena usar como base é aquele medido nos seus próprios templates usando o relatório por renderização do próprio motor. Um número sem o documento, o hardware e o método por trás dele é decoração, não dado.
O segundo equívoco é que a memória de pico é o que se deve observar. O pico é transitório e esperado — ele volta. O número que encerra um lote é a memória retida que não volta. É exatamente por isso que o motor separa os dois.
Limites e fronteiras
Seção intitulada “Limites e fronteiras”- Não existe um número universal de throughput, e esta página deliberadamente não declara nenhum. O custo de renderização depende dos seus documentos; meça usando o relatório por renderização.
- A memória limitada depende do uso do modelo de documento descartável. Manter um documento ao longo de muitas renderizações, ou compartilhar estado mutável por renderização, anula a garantia. As bridges de framework adotam por padrão o formato seguro. O wiring manual precisa replicá-lo.
- O cache de imagens é limitado, não ilimitado. Sob cargas pesadas de imagens únicas, o LRU faz despejo. Isso faz parte do design, não é uma regressão.
- O dimensionamento do pool de workers, a escolha da fila e o autoscaling são decisões de implantação fora do motor. O NextPDF fornece as medições e a primitiva limitada. Ele não executa sua fila.
- O
RenderReporté dado, não um veredito. Ele informa o que aconteceu em uma renderização. Transformar isso em um plano de capacidade é a sua análise. - Esta página é respaldada por benchmark para a superfície de medição e respaldada por código para o modelo de memória. Ela não afirma nenhuma taxa específica.
| Edition | Availability |
|---|---|
| Core | O modelo de documento descartável, os registries imutáveis compartilhados, o
|
| Pro | As mesmas primitivas; recursos comerciais (assinatura, PDF/A) adicionam custo por renderização que você deve medir, não presumir. |
| Enterprise | As mesmas primitivas; o trabalho com notas fiscais estruturadas e validação adiciona ainda mais custo por renderização que escala com o payload e o tamanho do conjunto de regras. |
Documentos relacionados
Seção intitulada “Documentos relacionados”- Memória e streaming — como o motor mantém a memória limitada em documentos grandes e onde ele faz streaming.
- Benchmarking honesto — o valor de um número de benchmark sem o seu método, e como o NextPDF reporta desempenho.
- Operando o NextPDF em produção — transformando relatórios por renderização em sinais de saúde quando o lote roda de verdade.
Glossário
Seção intitulada “Glossário”- Documento descartável — uma instância de documento criada para uma única renderização e descartada depois, de modo que nenhum estado vaze para a próxima renderização.
- Registry compartilhado — estado de duração do processo, imutável após o aquecimento (fontes, cache de imagens), reutilizado entre renderizações sem custo por renderização.
- Memória de pico — a marca máxima transitória durante uma renderização; é esperada e retorna ao baseline.
- Memória retida — memória ainda mantida depois que uma renderização termina; um baseline de memória retida crescente ao longo das renderizações é um vazamento.
- Worker — um processo de vida longa que puxa jobs de renderização de uma fila; precisa permanecer com memória limitada para sobreviver a um lote.
- RenderReport — o snapshot imutável de métricas por renderização do motor (tempo, memória de pico, contagem de páginas, avisos) usado para dimensionar capacidade a partir de dados reais.