You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: both failure modes measured, and whether absorbing an OOM recovers
Prompted by two review questions I could not answer from the code, so I
measured instead of reasoning. Both changed what the javadoc claims.
1. Is the OPIK-8164 failure an OOM? NO. Through the shipped JAVA codec's
value decoder at the real 20,000,000 limit:
19,999,999 chars -> decodes fine
20,000,001 chars -> StreamConstraintsException, isError=false
An ordinary IOException, matching the production stack trace verbatim.
The Exception arm has always handled the literal incident; my previous
commit framed the OOM arm as if it closed a hole related to it, which
was wrong.
2. But both modes are real, on the SAME path. A payload UNDER
maxStringLength but larger than the heap OOMs inside Jackson's own
String materialization. On a -Xmx64m fork, 12,000,000 chars (well under
the 20,000,000 limit) gave cause=java.lang.OutOfMemoryError,
isError=true, absorbed into the sentinel. This is the case that makes
the OOM arm matter in production, where maxStringLength ships at 100 MB
and consumerBatchSize is 10.
3. Does absorbing an OOM recover? For this shape, yes, measured: three
consecutive rounds of oversized-then-ordinary on a 64 MB heap each
absorbed the OOM and then decoded the ordinary message correctly, free
heap stable, no cumulative degradation. The array that failed was never
allocated, so the failure consumed nothing.
The operational argument settles it: the helm chart ships
-XX:+UseG1GC -XX:MaxRAMPercentage=80.0 with NO
-XX:+ExitOnOutOfMemoryError, so the process already survives an OOM
today. Not absorbing does not buy a clean restart -- it buys a
still-running pod with a permanently wedged stream. If the team ever
adds ExitOnOutOfMemoryError the JVM exits at throw time and this arm
becomes unreachable, which is fine.
Documented what it does NOT claim: a JVM under genuine heap exhaustion
is not healthy, this arm can absorb an OOM that was a symptom rather
than a cause, and the recovery measured is single-threaded.
Tests: the over-limit case now asserts the cause is StreamConstraintsException
and is NOT an Error, which is the distinction both questions turned on. Also
removed a shipped-codec test I had just added -- it needed an assumeTrue on
the memoized limit and skipped in the real suite, because RedisStreamCodecTest
calls JsonUtils.configure in the same JVM. The small-limit mapper exercises
the identical path; the real-limit numbers live in the javadoc as measured
evidence instead.
58 green, 0 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments