Commits
Tarun Sukhani authored 6274b4a2d0f
perf: resolve optional compression codecs once instead of per pipeline PF-173: buildHttpContentCompressor probed for the optional codecs by calling StandardCompressionOptions.brotli()/.zstd() and catching the failure, on every pipeline construction — that is once per accepted connection, for the life of the JVM. ZstdOptions' static initializer fails when zstd-jni is absent, the JVM marks the class erroneous, and every later touch re-throws a fresh NoClassDefFoundError with a full stack capture, so the cost never tapers off after warm-up. JFR on a JClaw run counted the throws tracking connections rather than requests, which is exactly what a per-pipeline probe predicts. Resolution moves into a lazily-initialized CompressionOptionsHolder, so the array is built once and every compressor is handed the same instance. Held in a nested class rather than a static field so it stays lazy: the compressor is not in the default play.netty.pipeline, and eager init would load brotli4j's native library at boot on deployments that never compress. The probe itself is replaced with Netty's own Brotli/Zstd.isAvailable(), which are cached booleans computed in their own static initializers and never touch BrotliOptions/ZstdOptions — the classes whose initializers throw. Caching alone would still pay the throw once per codec and leave the erroneous-class landmine armed; this way there is no NoClassDefFoundError at all. Both probes are guarded so a brotli-less deployment does not inherit the old behaviour. The per-codec catch stays as a net for a classpath that resolves the loader but fails to construct, and guarantees the holder's initializer cannot throw — an ExceptionInInitializerError there would be cached forever and recreate this bug class permanently. The compressor stays per-pipeline: it extends HttpContentEncoder, which keeps a per-channel ChannelHandlerContext and encoder queue and is not @Sharable, so the options array was the only hoistable per-call work. Sharing it is safe — Netty's ctor reads the array into per-type final fields and neither retains nor mutates it, and the options objects hold only final primitives. Adds HttpContentCompressorCacheTest. The bug is functionally invisible (compression still falls back to gzip/deflate correctly), so the tests assert on object identity across two separately built compressors, via the real public entry point. Verified red against the pre-PF-173 body. Optional-codec expectations are derived from Netty's availability flags rather than hardcoded, so the suite holds on either classpath. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ma8qUZG39DAkht2oSA7am2