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
refactor: absorb OutOfMemoryError instead of pre-checking the LZ4 declared length
Replaces LengthBoundedLz4Codec (net -102 lines). The ceiling it enforced
was an undocumented number of my own choosing: nothing in Redis or
Redisson constrains a stream field name, so 4096 rested only on the local
observation that every field name in this codebase is the constant
"message" -- an assumption that would silently discard data the day
someone picks a longer one.
Measured what the raw LZ4CodecV2 decoder actually does per declared
length, which showed the pre-check was doing much less than its javadoc
implied (9 GB heap):
negative -> NegativeArraySizeException (Exception, already caught)
7 (legitimate) -> IOException (Exception, already caught)
1,886,151,017 -> IOException (allocated 1.8 GiB, then failed)
Integer.MAX_VALUE -> OutOfMemoryError (Error, escaped)
Only the last row needed guarding, and which row a length lands in moves
with -Xmx: 1.8 GiB is an IOException on a 9 GB heap and an
OutOfMemoryError on a 1 GB container. So the fix is to widen the catch to
Exception | OutOfMemoryError, which makes the behaviour heap-independent
without inventing a limit. No other Error is absorbed -- StackOverflowError
and an OOM from a legitimate multi-hundred-MB payload document still
propagate.
Two residual risks, documented in the javadoc rather than papered over: an
allocation large enough to fail can starve a different thread, which then
throws where nothing catches it; and a length the heap can satisfy is
still allocated in full before it fails. Both are properly fixed by the
CompositeCodec argument-order change, which takes LZ4 off the field-name
path and dissolves the hazard instead of bounding it.
Tests: the four ceiling tests collapse into one parameterized case
asserting the sentinel for negative, MIN_VALUE, 1.8 GiB and MAX_VALUE
lengths, plus a direct OOM-absorption test and a StackOverflowError
propagation test for the boundary. Mutation check on the OOM arm does not
fail an assertion -- it kills the forked JVM (Tests run: 0, BUILD FAILURE),
which is a stronger signal but easy to misread, so noting it here.
55 green across the four affected suites.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments