Production Rendering

June 11, 2026 · View on GitHub

GraphCompose v1.2 is designed for request-scoped rendering. The library should not keep user CV text, addresses, emails, or generated PDF bytes in global state.

Request Lifecycle

Create one DocumentSession per render request and close it with try-with-resources:

try (DocumentSession document = GraphCompose.document().create()) {
    document.pageFlow(page -> page
            .module("Summary", module -> module.paragraph(summary))
            .module("Skills", module -> module.bullets(skills)));

    document.writePdf(responseOutputStream);
}

DocumentSession is mutable and not thread-safe. Do not share one session between requests or worker threads.

Streaming First

Use writePdf(OutputStream) when the next hop is already a stream:

  • HTTP response bodies
  • S3/blob/object-storage uploads
  • file output opened by the application
  • message or job pipelines that own their own stream

writePdf(...) writes the complete PDF to the supplied stream and does not close it. The application owns flushing, closing, cancellation, and backpressure.

Use toPdfBytes() only when the caller truly needs a byte array. It is a convenience wrapper over the streaming path and does not keep generated bytes in the session.

buildPdf(Path) also uses the streaming path internally.

Debug Output

Use GraphCompose.document(path).guideLines(true), document.guideLines(true), or the fuller document.debug(DocumentDebugOptions.guidesAndNodeLabels()) — which adds semantic node-path badges on top of the guides — only for local diagnostics, visual tests, and layout debugging. Debug overlays are render-only and do not change pagination or layout snapshots. Production services should normally leave them disabled.

Cache And Privacy Policy

GraphCompose may share immutable font/style metrics globally because those keys do not contain user document text.

User text width measurements are request/session-local. They are bounded and cleared when the session closes. Generated PDF bytes are not cached in DocumentSession.

Application logging should follow the same rule: log ids, counts, timings, and error types, but do not log CV text, emails, phone numbers, addresses, links, or full generated output paths.

Load Control

GraphCompose does not create an application-level worker pool. Production services should control concurrency outside the library:

  • use a bounded executor or web-server worker pool
  • apply queue limits and request timeouts
  • stream output instead of collecting every PDF into memory
  • apply tenant/user rate limits where needed
  • treat failed renders as request failures, not reusable session state

This keeps the engine stateless across requests while still allowing safe internal reuse of font metrics and backend resources during a single render pass.