fix(server): align cache event actions in legacy EventHub path#3017
fix(server): align cache event actions in legacy EventHub path#3017dpol1 wants to merge 3 commits intoapache:masterfrom
Conversation
- Align legacy cache invalidation producers and listeners on ACTION_INVALID and ACTION_CLEAR, removing the obsolete ACTION_INVALIDED/ACTION_CLEARED constants. - Add EventHub.notifyExcept(...) so cache transactions and the cache notifier bridge can avoid re-processing their own local listener while still delivering events to other listeners. - Track registered graph/schema cache listeners per graph so notifyExcept(...) uses the listener instance actually registered on the EventHub, including multi-transaction cases where later transactions reuse the first listener. - Update cache notifier forwarding to prevent local RPC bridge loops after action names are unified. - Add regression coverage for notifyExcept semantics, graph/schema action names, listener teardown/re-registration, and notifier no-loop behavior.
|
@imbajin I took the liberty to quickly fix a flaky test which failed a workflow twice - if you think is out of the scope with this PR I' ll revert it - Actually, I just noticed the first commit is all greens |
There was a problem hiding this comment.
Pull request overview
This PR fixes a legacy cache invalidation bug where cache event producers emitted past-tense action names (invalided/cleared) that didn’t match the actions local listeners (and some other paths) were expecting (invalid/clear), causing silent drops and stale caches in certain deployments. It also introduces EventHub.notifyExcept(...) to prevent cache-notification bridges from re-consuming their own re-emitted events.
Changes:
- Remove legacy
Cache.ACTION_INVALIDED/Cache.ACTION_CLEAREDand align all cache event producers/listeners toCache.ACTION_INVALID/Cache.ACTION_CLEAR. - Add
EventHub.notifyExcept(event, ignoredListener, args...)and routenotify(...)through the same internal implementation. - Track per-graph cache listeners in static registries and use
notifyExcept(...)to avoid self-delivery/bridge loops; add/adjust unit tests for the new semantics.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/Cache.java | Removes past-tense cache action constants to enforce a single action vocabulary. |
| hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/event/EventHub.java | Adds notifyExcept(...) and implements ignored-listener filtering in the notifier loop. |
| hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/event/EventHubTest.java | Adds coverage for notifyExcept(...) (including ANY_EVENT listeners). |
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java | Updates legacy notifier bridge to present-tense actions and uses notifyExcept(...) to avoid loops. |
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java | Replaces the boolean guard with a per-graph cache listener registry map. |
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CachedSchemaTransaction.java | Emits present-tense actions and uses per-graph listener registry + notifyExcept(...) on emits. |
| hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CachedGraphTransaction.java | Emits present-tense actions and uses per-graph listener registry + notifyExcept(...) on emits. |
| hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/cache/CachedSchemaTransactionTest.java | Adds tests validating action name alignment, listener registry lifecycle, and bridge no-loop behavior. |
| hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/cache/CachedGraphTransactionTest.java | Adds tests validating action name alignment and “non-owner close” listener behavior. |
| hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/cache/CacheManagerTest.java | Speeds up expiration tick test via reflective rescheduling and cancels the ad-hoc task. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
imbajin
left a comment
There was a problem hiding this comment.
Fix looks correct and the new tests are solid. Two concerns worth tracking: (1) storeEventListenStatus has the same ownership bug this PR fixes for the cache listener, acknowledged in the test's finally block — worth a follow-up; (2) the owner-first-close scenario for the cache listener deserves an explicit test and/or a ref-counted ownership model.
- Replace the per-transaction cache listener owner flag with a per-graph CacheListenerHolder shared by graph and schema cache transactions. - The holder keeps the EventHub listener registered while any transaction for the graph is alive, and unregisters/removes it only when the last transaction releases it. The registry update, ref-count decrement, and hub unlisten now run inside ConcurrentMap.compute() to avoid owner-closes-first invalidation gaps. Also add graph/schema regression coverage for owner-first close and last-close cleanup, including graph close/reopen handling for stale EventHub holders.
b77fb66 to
80bd29c
Compare
Purpose of the PR
Cache producers in the legacy
EventHubpath were emitting past-tense actionnames (
invalided/cleared) while local cache listeners only matched thepresent-tense actions (
invalid/clear). On single-node deployments, orwhenever the cache RPC bridge in
StandardHugeGraph.AbstractCacheNotifierwasnot active, those local cache invalidation events were silently dropped: no
error, no warning, just stale cache.
The fix aligns producers, listeners, and the bridge on the present-tense
constants, removes the obsolete past-tense ones, and wires
notifyExcept(...)so the bridge does not loop back into itself once both ends speak the same
action name.
Main Changes
Cache.ACTION_INVALIDED/Cache.ACTION_CLEARED. All producers(
CachedGraphTransaction,CachedSchemaTransaction) now emitCache.ACTION_INVALID/Cache.ACTION_CLEAR, matching what local listenersand
RaftContextalready expect.StandardHugeGraph.AbstractCacheNotifierto listen on thepresent-tense actions and to forward through
EventHub.notifyExcept(...)sothe bridge does not re-receive its own re-emit.
EventHub.notifyExcept(event, ignoredListener, args...)so a caller cannotify everyone on the hub except the listener that originated the event.
The signature is documented as a public API on
EventHub,notify(...)is unchanged and now delegates internally.
notifyExcept(...)from the cache transactions and fromAbstractCacheNotifier, with the registered cache listener as the ignoredone, so producers do not feed their own listener and the RPC re-emit does
not loop the bridge.
GraphTransaction.graphCacheEventListenersand in a matching map insideCachedSchemaTransaction, with owner-aware teardown on close. This is theminimum needed for
notifyExcept(...)to exclude the listener that isactually registered on the hub: with the old per-instance lambda +
containsListener(...)/ boolean guard pattern, only the first transactionper graph registered a listener, while later transactions could call
notifyExcept(...)with a lambda that was never registered. In that case thereal registered listener was not excluded and could still process the
transaction's own local emit.
CachedSchemaTransactionV2is intentionally not touched here. That class wasjust adjusted in #3011 for HStore/V2 schema cache propagation through
MetaManager. The remainingcontainsListener(...)guard there is a relatedlistener-lifecycle concern, not the action-name mismatch fixed in this PR; it
should be handled in a follow-up that audits the remaining V2 path and, if
needed, moves cache listener ownership to a ref-counted holder keyed by graph
identity and
EventHub.Verifying these changes
EventHubTest#testNotifyExceptcovers the newnotifyExcept(...)semantics, including
ANY_EVENTlisteners and the ignored-listenerexclusion.
CachedSchemaTransactionTest#testLegacySchemaChangeEmitsActionInvalidasserts the schema producer now emits
ACTION_INVALIDend-to-end.CachedSchemaTransactionTest#testCacheListenerSurvivesOwnerCloseasserts that closing the owner transaction does not drop the hub
listener while a second transaction is still alive, and that cache
invalidation events are still delivered to the surviving transaction.
CachedSchemaTransactionTest#testLastCloseRemovesSchemaCacheListenerasserts that the hub listener is unregistered and the registry entry
removed only when the last transaction for that graph closes, and that
a subsequent graph open registers a fresh holder.
CachedGraphTransactionTest#testCacheListenerSurvivesOwnerClosesame owner-first-close guarantee for the graph vertex/edge cache path.
CachedGraphTransactionTest#testLastCloseRemovesGraphCacheListenersame last-close cleanup guarantee for the graph cache path.
CachedSchemaTransactionTest#testCacheNotifierLocalEventCallsProxyOnceand
testCacheNotifierRpcInvalidDoesNotLoopToProxycover the bridgeno-loop behavior.
CachedGraphTransactionTest#testClearCacheEmitsActionClearassertsgraph cache clears now emit
ACTION_CLEAR.CachedGraphTransactionTest#testVertexMutationEmitsActionInvalidasserts graph vertex mutations now emit
ACTION_INVALID.CachedGraphTransactionTest#testClosingNonOwnerKeepsGraphCacheListenerRegisteredasserts that closing a graph transaction which reused an existing
per-graph listener does not unregister the shared graph cache listener.
Does this PR potentially affect the following parts?
EventHub.notifyExcept(...)is a new public method; existingnotify(...)behavior is unchanged.
Documentation Status
Doc - TODODoc - DoneDoc - No Need