Skip to content

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

Merged
imbajin merged 4 commits into
apache:masterfrom
dpol1:fix/3033-store-listener-leak
Jun 17, 2026
Merged

fix(core): ref-count store event listener to fix owner-first close leak#3058
imbajin merged 4 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.
  • 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.

Breaking change (internal API)

This removes the protected static final ConcurrentHashMap<String, Boolean> storeEventListenStatus field from GraphTransaction. The removal is
intentional and mirrors #3017, which already removed the sibling
graphCacheListenStatus (same protected static, same 1.7.0 vintage) with no
compatibility shim when graph-cache listening moved to the ref-counted holder.

  • Scope: internal listen-tracking state consumed only by the in-tree
    CachedGraphTransaction; no external extension point uses it.
  • No deprecated shim: a @Deprecated no-op field cannot preserve the old
    per-graph boolean semantics (now replaced by ref-counting), so it would be
    dead, misleading static state.
  • Migration: none required unless external code subclasses
    GraphTransaction and reads this internal field directly. Store-event
    listening is now handled transparently by STORE_EVENT_LISTENERS.

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?

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 previously approved these changes Jun 11, 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: 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.

@VGalaxies VGalaxies left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review summary

  • Blocking: no
  • Summary: One low-severity compatibility risk remains from removing a protected field.
  • Evidence:
    • git diff --unified=0 origin/master...HEAD -- hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java

…tenStatus

Document in-source that the ref-counted STORE_EVENT_LISTENERS registry
supersedes the removed protected static storeEventListenStatus field on
GraphTransaction, addressing the API-removal review thread on apache#3058.
@dpol1

dpol1 commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

Addressed - Also updated PR description

@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: git diff --check; CachedGraphTransactionTest passed locally (13 tests); latest-head checks pass.

@imbajin imbajin merged commit c3f56b5 into apache:master Jun 17, 2026
15 checks passed
@dpol1 dpol1 deleted the fix/3033-store-listener-leak branch June 17, 2026 06:02
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

3 participants