Skip to content

Fix resource safety issues in topic message decoder - #710

Open
Myllyenko wants to merge 2 commits into
ydb-platform:masterfrom
Myllyenko:codex/topic-decoder-safety
Open

Myllyenko wants to merge 2 commits into
ydb-platform:masterfrom
Myllyenko:codex/topic-decoder-safety

Conversation

@Myllyenko

@Myllyenko Myllyenko commented Aug 24, 2026

Copy link
Copy Markdown

1. Guard the ready handler and validate the buffer size

Change Old behavior New behavior
Buffer configuration validation A zero or negative maxBufferSize produced a decoder that can never admit a message, because DecodeNext only runs while totalAvailable is positive. The reader stalled silently. Construction fails immediately with IllegalArgumentException.
Ready-handler failure readyHandler was invoked directly from EncodedMessage.decode() and from setError(). An exception thrown by the handler escaped into the decompression executor thread, where it is reported as an uncaught exception and can suppress decoding of the messages that follow. The handler is invoked through notifyReady(), which logs the failure after the message has already been marked as ready.

This commit changes no existing expectations — all pre-existing tests pass untouched.

2. Make the decompression buffer limit a hard limit

Change Old behavior New behavior
Buffer-limit enforcement DecodeNext admitted the next message whenever any budget remained, without looking at its size, so totalAvailable went negative and the decoder retained more uncompressed data than maxMemoryUsageBytes allows. A message is admitted only when it fits into the remaining budget. A message larger than the whole budget is still admitted, but only once nothing else retains the buffer, so the overshoot stays bounded by that single message.

This commit is up for discussion and can be dropped on its own. Four existing tests (flowControlByBudgetTest, partitionFlowTest, decodeStopTest, decodesOnProvidedExecutorTest) asserted the previous behavior explicitly through negative getTotalAvailable() values, which suggests the overshoot may be an intentional trade-off rather than an oversight. Their expectations are updated in this commit. The trade-off of the stricter rule: a message larger than the budget now waits until the buffer is fully released, and since one decoder is shared by all partitions of a reader, that wait can be longer when several partitions are active.

If the greedy admission is intended, dropping commit 2 leaves commit 1 applicable as is.

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.22%. Comparing base (d77889d) to head (aa48da9).

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #710      +/-   ##
============================================
+ Coverage     73.18%   73.22%   +0.03%     
- Complexity     3589     3592       +3     
============================================
  Files           392      392              
  Lines         16531    16546      +15     
  Branches       1736     1739       +3     
============================================
+ Hits          12099    12116      +17     
  Misses         3811     3811              
+ Partials        621      619       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Igor Melnichenko and others added 2 commits September 17, 2026 16:33
MessageDecoder accepted a non-positive maxBufferSize. Since DecodeNext only
admits messages while totalAvailable is positive, such a decoder never submits
anything and silently stalls the reader instead of failing fast. Construction
now rejects it with IllegalArgumentException.

ReadPartitionDecoder invoked readyHandler directly from EncodedMessage.decode()
and from setError(). An exception thrown by the handler escaped into the
decompression executor thread, where it is reported as an uncaught exception and
may suppress decoding of the messages that follow. The handler is now called
through notifyReady(), which logs the failure after the message has already been
marked as ready.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DecodeNext admitted the next message whenever any budget remained, without
looking at its size. A message was therefore submitted even when it did not fit,
so totalAvailable went negative and the decoder held more uncompressed data than
maxMemoryUsageBytes allows.

A message is now admitted only when it fits into the remaining budget. A message
larger than the whole budget would never fit, so it is still admitted, but only
once nothing else retains the buffer, which keeps the overshoot bounded by that
single message instead of letting it accumulate.

The existing tests asserted the previous behavior through the negative values of
getTotalAvailable(), so their expectations are updated accordingly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Myllyenko
Myllyenko force-pushed the codex/topic-decoder-safety branch from 1bf6a03 to aa48da9 Compare September 17, 2026 13:41
@Myllyenko Myllyenko changed the title Making topic message decoder resource-safe Fix resource safety issues in topic message decoder Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant