Skip to content

fix(core): ref-count store event listener to fix owner-first close leak#3058

Open
dpol1 wants to merge 3 commits into
apache:masterfrom
dpol1:fix/3033-store-listener-leak
Open

fix(core): ref-count store event listener to fix owner-first close leak#3058
dpol1 wants to merge 3 commits into
apache:masterfrom
dpol1:fix/3033-store-listener-leak

Conversation

@dpol1

@dpol1 dpol1 commented Jun 10, 2026

Copy link
Copy Markdown
Member

Purpose of the PR

close #3033

storeEventListenStatus had the same owner-first-close bug #3017 fixed
for graph cache listeners: non-owner close() dropped the tracking entry
and skipped unlisten() as a no-op, leaving the owner's listener
registered but untracked — leak + no store-event cache invalidation.

Main Changes

  • CachedGraphTransaction: add StoreListenerHolder inner class
    (listener, provider, refCount) + STORE_EVENT_LISTENERS static
    registry; acquire/release via ConcurrentMap.compute() with
    provider-identity guard for graph close/reopen. TODO block removed.
    Drop write-only storeEventListener instance field — listener
    ownership moved to StoreListenerHolder, field was assigned but
    never read.
  • GraphTransaction: remove storeEventListenStatus declaration
    and orphaned ConcurrentHashMap import. Note: storeEventListenStatus
    was protected static — minor API surface removal, internal only,
    no known external use.
  • CachedGraphTransactionTest: delete
    restoreStoreListenerStatusForKnownTeardownBug workaround; repoint
    reflection helper to STORE_EVENT_LISTENERS; add 5 regression tests:
    non-owner close keeps listener registered, last close removes listener,
    owner-first close with real STORE_CLEAR fires through provider and
    surviving tx clears cache, reopen re-registers a fresh holder. Provider
    is pooled across reopen — provider-replacement branch stays defensive,
    not exercised.

CachedSchemaTransaction* unaffected — per-instance, balanced 1:1.

Verifying these changes

  • Need tests and can be verified as follows:
    • RED/GREEN TDD: new tests failed on master (NoSuchFieldException on
      STORE_EVENT_LISTENERS), green after fix.
    • 13/13 pass: CachedGraphTransactionTest
    • 36/36 pass: CachedGraphTransactionTest, CachedSchemaTransactionTest,
      CacheManagerTest, CacheTest

Does this PR potentially affect the following parts?

  • Nope

Documentation Status

  • Doc - No Need

…ak (apache#3033)

storeEventListenStatus had the same owner-first-close bug apache#3017 fixed for
graph cache listeners: non-owner close() dropped the entry and skipped
unlisten() as a no-op, leaking the owner's listener.

Apply CacheListenerHolder ref-count pattern: StoreListenerHolder +
STORE_EVENT_LISTENERS in CachedGraphTransaction, acquire/release via
compute() with provider-identity guard for close/reopen.

Removes storeEventListenStatus from GraphTransaction and
restoreStoreListenerStatusForKnownTeardownBug workaround from tests.
CachedSchemaTransaction* unaffected (per-instance, balanced 1:1).
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working tests Add or improve test cases labels Jun 10, 2026

@imbajin imbajin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: no. Summary: Please add one store-listener lifecycle test for the owner-first close path. Evidence: static review of CachedGraphTransactionTest; local Maven test was blocked by unresolved ${revision} dependency while current CI checks pass.

dpol1 added 2 commits June 10, 2026 16:51
- Add testStoreListenerSurvivesOwnerClose: close the owner transaction
  first, fire STORE_CLEAR through the provider, and assert the surviving
  transaction still clears its cache via the ref-counted provider listener.

- Add testReopenGraphReRegistersStoreListener: assert last close drops the
  store registry entry and unregisters the listener, and reopen re-registers
  a fresh holder. The provider instance is pooled across reopen, so the
  provider-replacement branch stays defensive and is not exercised here.
The ref-count refactor moved listener ownership into StoreListenerHolder,
leaving the storeEventListener instance field assigned but never read.
Remove it; cacheEventListener stays as notifyChanges() still reads it.
@dpol1 dpol1 requested a review from imbajin June 10, 2026 15:17

@imbajin imbajin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: no. Summary: No obvious issues found in the current head. Evidence: CachedGraphTransactionTest passed locally and latest-head CI checks are green.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jun 11, 2026
@dpol1

dpol1 commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

@imbajin This PR covers the CGT fix. A follow-up improvement for CachedSchemaTransaction will be tracked separately. Not a bug or a hurry issue, but an improvement that will be calmly implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files. tests Add or improve test cases

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] storeEventListenStatus leaks shared store listener when a non-owner CachedGraphTransaction closes first

2 participants