Skip to content

Release the flush scheduler when a batch storer is abandoned without close()#307

Merged
fh-ms merged 1 commit into
mainfrom
fix/abandoned-batch-storer-leak
Jul 21, 2026
Merged

Release the flush scheduler when a batch storer is abandoned without close()#307
fh-ms merged 1 commit into
mainfrom
fix/abandoned-batch-storer-leak

Conversation

@fh-ms

@fh-ms fh-ms commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

A Batching storer (BinaryStorer.Batching, the BatchStorer implementation) scheduled its background flush as a bound method reference (this::backgroundFlush). Because the scheduled task strongly referenced the storer, the executor's worker thread — a GC root — pinned the storer for the lifetime of the JVM. A storer that was dropped without calling close() therefore leaked its direct chunk buffers, hash slots and trusted-object-id set, plus one background daemon thread, per abandoned instance. The non-batching storers are held only weakly by the object manager and are collectable when abandoned, so drop-and-forget looked safe.

Fix

Move the executor into a BackgroundFlusher helper that holds the storer through a WeakReference:

  • The periodic task retained by the executor's work queue now references only the flusher (which holds the storer weakly), so the worker thread no longer pins the storer. An abandoned storer becomes garbage-collectable immediately.
  • On the next tick the task observes the cleared referent and shuts the executor down — on the executor thread, so without awaitTermination (which would dead-lock) — reclaiming the daemon thread.
  • close() delegates its graceful shutdown (shutdown → awaitTerminationshutdownNow) to the flusher; the public close() behavior, including the final flush, is unchanged.

This mirrors the existing idiom in the tree: EvictionManager.IntervalThread and the JVector BackgroundTaskManager.

Testing

A deterministic regression test lives in the eclipse-store repository (test.eclipse.store.various.storer.BatchStorerAbandonedLeakTest) and covers both symptoms — leaked batch-storer-flush threads and the storer instance staying reachable after a full GC sweep (with a non-batch storer as a control). It fails against the previous code and passes with this change. The existing BatchStorer* suite continues to pass.

No public API change.

…close()

A Batching storer scheduled its background flush as a bound method reference (this::backgroundFlush), so the executor's worker thread — a GC root — strongly pinned the storer. A storer dropped without close() therefore leaked its chunk buffers, hash slots and trusted-object-id set plus one daemon thread for the JVM lifetime, even though the non-batching storers are collectable when abandoned.

Move the executor into a BackgroundFlusher helper that holds the storer through a WeakReference. The periodic task now references only the flusher, so an abandoned storer becomes collectable immediately; the next tick observes the cleared referent and shuts the executor down (on the executor thread, without awaitTermination), reclaiming the daemon thread. close() delegates its graceful shutdown to the flusher, preserving the previous behavior. This mirrors EvictionManager.IntervalThread and the JVector BackgroundTaskManager.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a memory/thread leak in BinaryStorer.Batching where scheduling a bound method (this::backgroundFlush) caused the scheduled executor thread to strongly retain the storer, preventing GC when abandoned without close().

Changes:

  • Replaced the inlined ScheduledExecutorService field with a BackgroundFlusher helper that holds the storer via WeakReference.
  • Updated shutdown logic so close() delegates graceful executor termination to the flusher.
  • Added documentation explaining the retention/leak mechanism and the self-termination behavior when the storer is GC’d.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@fh-ms
fh-ms requested a review from zdenek-jonas July 21, 2026 11:25
@fh-ms
fh-ms merged commit 56a005e into main Jul 21, 2026
20 checks passed
@fh-ms
fh-ms deleted the fix/abandoned-batch-storer-leak branch July 21, 2026 11:49
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.

3 participants