dosubot[bot] commented on issue #3033:
URL: https://github.com/apache/hugegraph/issues/3033#issuecomment-4497259980
<!-- Answer -->
Here's what I found across all your questions:
***
**CacheManager is a JVM-wide singleton, not per-graph.** Per-graph isolation
is achieved through naming: `"{prefix}-{spaceGraphName}"` passed to
`CacheManager.instance().cache(name, capacity)`
[[1]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CacheManager.java#L37).
**Cache creation call sites** (all go through the singleton):
| Component | Call Site | Pattern |
|-----------|-----------|---------|
| `CachedGraphTransaction` | Constructor | `"vertex-" + spaceGraphName`,
`"edge-" + spaceGraphName`
[[2]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CachedGraphTransaction.java#L100)
|
| `CachedSchemaTransaction` | `cache()` helper | `"{prefix}-" +
graph().spaceGraphName()`
[[3]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CachedSchemaTransaction.java#L92-L94)
|
| `CachedSchemaTransactionV2` | Same pattern |
[[4]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CachedSchemaTransactionV2.java#L89-L91)
|
| `StandardAuthManager` | Constructor | `"{prefix}-" +
graph.spaceGraphName()`
[[5]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/auth/StandardAuthManager.java#L130-L131)
|
***
**Natural lifecycle hooks for Option 2** — `StandardHugeGraph` has these
methods where listener registration could live
[[6]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java):
- **Register at:** `initBackend()`
[[7]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java#L475-L497)
— runs once when the graph store is first initialized. Alternatively, inside
the constructor/`open()` path where `EventHub` instances are already created
[[8]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java#L194-L278).
- **Unregister at:** `close()`
[[9]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java#L1087-L1115)
— synchronized, runs once during shutdown. It already calls `closeTx()` →
destroys transactions, and then closes the store provider. Inserting
`provider().unlisten(...)` before `storeProvider.close()` would be the natural
spot.
- `drop()` → calls `clearBackend()` then `close()`, so close covers it
[[10]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java#L1129-L1148).
The key insight: `closeTx()`
[[11]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java#L389-L397)
destroys the per-thread transaction instances, but the store provider outlives
them until `close()` finishes. Placing listener management at the graph level
removes the multi-transaction race entirely.
***
**CachedSchemaTransactionV2** — yes, it exists
[[12]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CachedSchemaTransactionV2.java),
used for HStore backends. The selection is in
`StandardHugeGraph.openSchemaTransaction()`
[[13]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java#L598-L603):
```java
if (isHstore()) {
return new CachedSchemaTransactionV2(...);
}
return new CachedSchemaTransaction(...);
```
**It does NOT have a `storeEventListenStatus` field.** It stores listener
refs directly as instance fields
[[14]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/cache/CachedSchemaTransactionV2.java#L51-L52):
```java
private EventListener storeEventListener;
private EventListener cacheEventListener;
```
Because there's no shared static tracking map, V2 doesn't suffer the *same*
T1+T2 race — **but** it does call `unlisten()` unconditionally on every
transaction close, mirroring the `CachedSchemaTransaction` behavior. If the
underlying `AbstractBackendStoreProvider.unlisten()` is idempotent (which it is
— it just removes from an EventHub), the V2 path is safe for its own close
ordering, but it registers on *every* instance construction, which could mean
redundant registrations rather than leaks.
***
**Thread safety: is `ConcurrentHashMap.compute()` sufficient?**
The `storeEventListenStatus` is a `static ConcurrentHashMap<String,
Boolean>` in `GraphTransaction`
[[15]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphTransaction.java#L145-L146).
Multiple concurrent transaction instances for the same graph **absolutely
exist** — each thread gets its own via ThreadLocal
[[16]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java#L1781-L1813).
`ConcurrentHashMap.compute()` **is sufficient** for the ref-counted holder
pattern (Option 1), because:
- `compute()` guarantees atomicity for the lambda — no two threads execute
the lambda for the same key concurrently.
- The `CacheListenerHolder` pattern from PR #3017 already does this
successfully: it puts the `listen()`/`unlisten()` calls *inside* the
`compute()` lambda, making the check-and-act atomic.
- Since `provider().listen()` and `unlisten()` just delegate to
`EventHub.listen()`/`EventHub.unlisten()`
[[17]](https://github.com/apache/hugegraph/blob/9126c80e414c073b60fc074361b211cfb409e0eb/hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/store/AbstractBackendStoreProvider.java#L69-L76),
which are lightweight map operations, executing them inside `compute()`
doesn't create deadlock risk.
You do **not** need an external `synchronized` block — `compute()` handles
per-key exclusion. The proven pattern from PR #3017's
`GRAPH_CACHE_EVENT_LISTENERS` demonstrates this works correctly.
***
**Summary for your fix scope:**
| Class | Has tracking field? | Affected by T1+T2 race? | Fix needed? |
|-------|-------------------|------------------------|-------------|
| `CachedGraphTransaction` | Yes (`storeEventListenStatus` static) | ✅ Yes |
✅ |
| `CachedSchemaTransaction` | No (unconditional unlisten) | Different bug
shape (redundant register/no-op unlisten) | ✅ |
| `CachedSchemaTransactionV2` | No (instance fields, no static map) |
Redundant registration, not a leak | Low priority |
If you go with **Option 2** (graph-level lifecycle), you eliminate the
problem in all three classes at once — the listener lives in
`StandardHugeGraph`, tied to `initBackend()`/`close()`, and the transactions
simply use the cache without managing listeners at all.
<!-- Dosu Comment Footer -->
*To reply, just mention
[@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=reply-with-mention&utm_term=apache%2Fhugegraph).*
---
Docs are dead. Just use
[Dosu](https://dosu.dev?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=static-docs-tagline&utm_term=apache%2Fhugegraph).
[](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=static-docs-feedback&utm_term=apache%2Fhugegraph&message_id=1778ad93-c743-40f9-b6b8-10b5386700a9)
[](https://github.dosu.com/apache/hugegraph?utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=static-docs-ask-repo&utm_term=apache%2Fhugegraph)
[](https://app.dosu.dev/signup?referrer=openSource&source=github-footer&utm_source=github&utm_medium=bot-comment&utm_campaign=github-comment-footer-20260415&utm_content=static-docs-share-team&utm_term=apache%2Fhugegraph)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]