Skip to content

Convert escaping Throwable in copyLogSegmentData to RemoteStorageException - #833

Open
bingkunyangvungle wants to merge 2 commits into
Aiven-Open:mainfrom
bingkunyangvungle:fix/wrap-escaping-throwable-820
Open

Convert escaping Throwable in copyLogSegmentData to RemoteStorageException#833
bingkunyangvungle wants to merge 2 commits into
Aiven-Open:mainfrom
bingkunyangvungle:fix/wrap-escaping-throwable-820

Conversation

@bingkunyangvungle

Copy link
Copy Markdown

Fixes the silent-exit class of failure described in #820.

Problem

If a Throwable escapes RemoteStorageManager.copyLogSegmentData — for example, an OutOfMemoryError from inside multipart-upload buffering, a NoClassDefFoundError, or a re-thrown Error from a finally block — it bypasses Kafka's RLMTask catch (Exception) clause (because Error is not an Exception) and trips the JDK contract for ScheduledThreadPoolExecutor.scheduleWithFixedDelay: "if any execution of the task encounters an exception, subsequent executions are suppressed." The partition's RLMTask permanently stops firing; local on-disk grows monotonically until broker restart or leader change.

Fix

Wrap the upload body in a try/catch so any escaping Throwable is converted to RemoteStorageException. Since RemoteStorageException extends Exception, the broker's existing catch swallows it and the schedule survives — the next 30s tick allocates a fresh RemoteLogSegmentId and retries.

Validation

Reproduced the wedge locally by injecting an OutOfMemoryError inside copyLogSegmentData on the unpatched plugin running against Kafka 3.7: the affected partition stopped uploading and accumulated local segments while sibling partitions continued normally. With this patch applied, the same injection produces a single error log line, the broker's existing catch (Exception) handles the wrapped exception, and the next scheduled tick succeeds.

Closes #820

…ption

When an Error (e.g. OutOfMemoryError, NoClassDefFoundError) or unchecked
Throwable escapes RemoteStorageManager.copyLogSegmentData, it bypasses
RLMTask's catch (Exception) on the Kafka side and trips
ScheduledThreadPoolExecutor.scheduleWithFixedDelay's silent-suppression
contract — all further executions for that partition are dropped, and copy
permanently stops until broker restart or leader change.

Wrap the body in a try/catch so any escaping Throwable is converted to
RemoteStorageException, which the broker's existing catch handles, allowing
the scheduled task to retry on the next tick.

Refs Aiven-Open#820
@bingkunyangvungle
bingkunyangvungle requested a review from a team as a code owner June 12, 2026 01:01
…ption

Covers four cases via Mockito mockConstruction of KafkaRemoteStorageManager:
- OutOfMemoryError -> wrapped as RemoteStorageException (cause preserved)
- NoClassDefFoundError -> wrapped as RemoteStorageException (cause preserved)
- IllegalStateException -> wrapped as RemoteStorageException (cause preserved)
- pre-existing RemoteStorageException -> passes through unchanged (no double-wrap)
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.

RemoteStorageManager.copyLogSegmentData() hangs indefinitely on intermittent uploads — never returns, never throws

1 participant