Skip to content

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

Open
dpol1 wants to merge 4 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 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.

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