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.