Output service PDF generation times out under batch load
A nightly batch generates 25,000 personalized PDFs using AEM Forms Output service on a 2-node AEM 6.5 author/publish cluster (each node 8 vCPU, 16 GB heap). Each PDF merges a 12-page XDP template with a 40-field XML record and applies a digital signature via a separate Assembler step. The batch is driven by a single OSGi service using OutputService.generatePDFOutput in a loop with a shared FormsServiceClient obtained once. Symptoms: heap climbs to 15.5 GB within 40 minutes, Full GC pauses of 8-12 s, and OutputServiceException: timeout after 60 s per record. Throughput is ~6 PDFs/min vs. the 400/min target. The signature step is synchronous and re-reads the same 3 MB keystore per record.
1. Identify the primary resource leaks and concurrency mistakes, and propose a corrected execution model.
Read the worked answer
Primary issues:
Unbounded in-memory PDFs.
generatePDFOutputreturns aDocument/byte[] held in the loop; if results are accumulated or not closed, 25k × multi-MB PDFs exhaust the heap. Stream each result to disk/object storage and null references.Shared client misuse. A single
FormsServiceClientreused across threads is not guaranteed thread-safe for all operations; and a single-threaded loop cannot hit 400/min. Use a bounded thread pool sized to CPU (e.g., 6-8 workers per node) with a client per worker or a documented thread-safe pool.Keystore re-read per record. Loading a 3 MB keystore 25k times dominates time; cache the keystore/credential in a singleton and reuse the signing credential.
Synchronous signature in the hot path. Separate generation and signing into stages, or sign in the same worker with cached credentials.
Corrected model: producer partitions the 25k records into chunks; a bounded executor (e.g., 6 workers/node) each owns its client and streams PDFs to storage; signing uses a cached credential; backpressure via a bounded queue prevents heap growth; monitor GC and queue depth. Expected throughput scales with workers and removes keystore I/O.
2. The team wants to raise the heap to 32 GB to 'fix' the timeouts. Assess this and list the tuning/observability changes you would make instead.
Read the worked answer
Raising heap to 32 GB is a poor first move: it delays GC, increases pause times, and masks the leak - 25k accumulated PDFs will still exhaust any heap. It also risks exceeding container limits and causing OOM kills.
Instead:
- Fix retention: stream results, close
Documentobjects, avoid holding per-record state. - Bound concurrency to CPU count; use a bounded queue for backpressure.
- Cache the keystore and any compiled template artifacts; reuse
FormsServiceClientper worker. - Tune GC for the workload (e.g., G1 with a target pause, or region sizing) and set
-XX:+HeapDumpOnOutOfMemoryError. - Observability: expose metrics for queue depth, active workers, PDFs/min, GC pause, and per-record latency; alert on timeout rate.
- Timeout policy: raise the 60 s timeout only after fixing the leak, and make it per-stage (generation vs. signing).
Validate with a 1,000-record canary before the full run.