fix(server): wait for GRAPH_CREATE event when creating graph on PD path - #3138
Conversation
The PD-backed createGraph fired GRAPH_CREATE without awaiting it, so the REST 200 could be written before ContextGremlinServer injected the graph into the Gremlin global bindings, and an immediate Gremlin/Cypher request to the creating server could fail with "Could not rebind [g]". createGraphLocal already waits via notifyAndWaitEvent; this applies the same call on the PD path.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #3138 +/- ##
============================================
- Coverage 39.18% 34.66% -4.52%
- Complexity 264 498 +234
============================================
Files 770 782 +12
Lines 65779 67088 +1309
Branches 8726 8956 +230
============================================
- Hits 25774 23256 -2518
- Misses 37244 41230 +3986
+ Partials 2761 2602 -159 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: The PD create path now waits for GRAPH_CREATE listeners, but listener failures remain non-fatal and the current head has a failing codecov/project check. Evidence: EventHub.notify() catches listener Throwable at hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/event/EventHub.java:200-205; final gate reports codecov/project FAILURE.
|
|
||
| // Let gremlin server and rest server context add graph | ||
| this.eventHub.notify(Events.GRAPH_CREATE, graph); | ||
| this.notifyAndWaitEvent(Events.GRAPH_CREATE, graph); |
There was a problem hiding this comment.
There was a problem hiding this comment.
Agreed, fixed in 9a4ac02: bounded future.get(30s), and InterruptedException is caught on its own and restores the interrupt status before failing. The drop path gets the same bounded wait but stays lenient, since the data is already gone when the event fires and TinkerPop's removeGraph throws for a graph the Gremlin server never bound.
30s is a constant rather than an option, as ServerOptions has nothing comparable; happy to promote it if you want it tunable.
One call I would rather leave to you: a timeout currently fails the create, so a merely slow listener breaks a create that actually worked. Treating a timeout as unknown (log loudly, do not fail) is the alternative.
Waiting for the GRAPH_CREATE future was not enough to prove that the graph was actually registered: EventHub swallows every throwable raised by a listener and resolves the future with the number of listeners that returned normally, so a listener that blew up looked exactly like a successful one. The create now compares the notified count with the registered listener count and fails when a listener did not complete. The wait is also bounded now instead of blocking forever, and an InterruptedException restores the thread's interrupt status before the failure is reported. Ordering is fixed along with it. On the PD path the graph is bound in the local gremlin/rest server context before its config is written to meta and broadcast, so a failed binding cannot leave a graph behind in meta for the other servers to converge on. A binding failure now unregisters the graph locally and closes it, the same cleanup a failed backend init already does, rather than dropping data that other servers may have bound successfully. On the local path the notify moved inside the existing try, which now also unregisters the graph before dropping it, so a failed binding leaves no closed graph behind in the context. The drop path keeps the lenient behaviour: the data is already gone when the event fires, so failing the request cannot undo anything and the listener state may legitimately be absent already.
|
Both points addressed in 9a4ac02, details in the inline replies. Two things left out on purpose:
There is no automated coverage for the new failure semantics: nothing exercises
|
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. The Phase 1 boundary is appropriate: this PR only needs to guarantee that the creating Server completes its local graph and TraversalSource bindings before returning 200; cross-replica readiness remains in #3137. The happy path now meets that scope, but the two local rollback races below can still leave stale bindings after a failed create. Codecov is intentionally ignored here.
- dispatch graph lifecycle events synchronously - roll back partial Gremlin registrations on failure - cover synchronous listener failure counting
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Ensures graph creation in distributed mode doesn’t return success before the local server has completed GRAPH_CREATE listeners (notably Gremlin bindings injection), preventing immediate follow-up Gremlin/Cypher requests from failing.
Changes:
- Introduces a synchronous event notification path (
EventHub.notifySync) and uses it during graph create to run listeners before publishing graph metadata. - Adds stricter listener-failure detection in
GraphManagerduringGRAPH_CREATE, with rollback/cleanup on failure. - Makes Gremlin bindings removal more defensive by avoiding removals when the binding doesn’t exist.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java | Synchronous GRAPH_CREATE notification + failure detection and rollback/cleanup adjustments |
| hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/auth/ContextGremlinServer.java | Defensive removal of graph and traversal source bindings |
| hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/event/EventHubTest.java | Adds unit test for new notifySync() behavior |
| hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/event/EventHub.java | Adds notifySync() and refactors listener iteration/notification logic |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
a8e7cd3 to
b676ca3
Compare
- clean local bindings after failed creation - preserve backend cleanup before graph unbinding - remove Gremlin bindings before closing graph
b676ca3 to
5569400
Compare
imbajin
left a comment
There was a problem hiding this comment.
Re-reviewed current head 5569400b2 against the Phase 1 scope. No blocking findings.
- Graph creation now completes and validates local REST/Gremlin registration before publishing or returning success.
- Failed creation rolls back partial bindings and local caches; Gremlin registration/removal is synchronized and identity-guarded.
- Existing asynchronous
EventHub.notify()behavior remains unchanged for other callers. - The PR description clearly separates the creating-server guarantee from cross-replica readiness, PD lifecycle state, distributed drop, and full same-name lifecycle serialization.
Verification: EventHubTest passes 20/20 on JDK 11, and the hugegraph-api reactor compiles successfully on JDK 11. The remaining lack of a direct GraphManager/Gremlin integration test is a non-blocking coverage gap.
- report attempted and successful listeners together - validate graph creation from one listener snapshot - cover listener mutation during synchronous dispatch
1790ee4 to
92997d8
Compare
imbajin
left a comment
There was a problem hiding this comment.
Re-reviewed current head 92997d893 with six independent adversarial lanes. No in-scope blocking findings.
The listener outcome check now derives attempted and successful counts from the same COW snapshot, while the existing asynchronous EventHub API retains its behavior. Local binding failure rollback, identity guards, and synchronized Gremlin registration/removal remain sound.
Verification: EventHubTest passes 21/21 on JDK 11 and the hugegraph-api reactor compiles successfully. Residual risks are explicitly scoped: no direct GraphManager/Gremlin integration test, no listener deadline, and post-bind PD metadata transactions remain follow-up work.
|
@copilot-pull-request-reviewer[bot] Validated the final head |
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. The local GRAPH_CREATE synchronization is sound, but PD graph creation still publishes local state before later metadata and schema steps that can fail without a unified rollback; the current head also has a failing codecov/project check. Evidence: GraphManager.java:1353-1393; gh pr checks -R apache/hugegraph 3138.
| graph.updateTime(timeStamp); | ||
|
|
||
| String graphName = spaceGraphName(graphSpace, name); | ||
| this.graphs.put(graphName, graph); |
There was a problem hiding this comment.
graphs.put() and the synchronous GRAPH_CREATE notification run before addGraphConfig, notifyGraphAdd, updateGraphSpaceConfig, and schema initialization. If any later step throws, this method exits without removing the local graph/bindings, closing and unregistering the graph, or compensating metadata already written, so the failed request can leave a graph that blocks retry and can be published to other servers. Please extend one rollback boundary across the publish/schema phase, with best-effort local binding and HugeFactory cleanup plus compensation for partially written PD metadata.
There was a problem hiding this comment.
@bitflicker64 Confirmed. This is a real follow-up gap: after local GRAPH_CREATE succeeds, a failure in PD metadata publication or schema initialization can still leave local graph/bindings and partially written metadata behind.
For the current PR, we are keeping this outside the minimal Phase 1 boundary (the creating Server's local bindings must be ready before 200). Please track it under #3137 as the Phase 3 graph-creation state-machine work, including compensating metadata cleanup and one rollback boundary across publication/schema initialization.
Marking this as a follow-up for now; I am leaving the thread unresolved until that work is addressed.
…#3137) Document that the creating Server is consistent at HTTP 200 after apache#3138, while cross-replica convergence and PD-owned creation remain open upstream. Co-authored-by: Cursor <cursoragent@cursor.com>
…#3137) Document that the creating Server is consistent at HTTP 200 after apache#3138, while cross-replica convergence and PD-owned creation remain open upstream. Co-authored-by: Cursor <cursoragent@cursor.com>
…#3137) Document that the creating Server is consistent at HTTP 200 after apache#3138, while cross-replica convergence and PD-owned creation remain open upstream.
Purpose
This is Phase 1 of #3137. It closes the local race on the Server that handles graph creation; cluster-wide readiness remains a follow-up.
In distributed mode (PD + HStore), graph creation previously returned HTTP 200 after scheduling
GRAPH_CREATE, but before the embedded Gremlin Server had necessarily registered the graph and itsTraversalSource. An immediate Gremlin or Cypher request to the same Server could therefore fail with:Before and after
After this PR, HTTP 200 guarantees that the creating Server has completed its local REST and Gremlin registrations, including the
__g_<space>-<graph>traversal binding.Changes
EventHub.notifySync()dispatch while preserving the existing asynchronousnotify()behavior for other callers.GRAPH_CREATElistener fails.Scope: intentionally not handled here
LOADING / READY / FAILEDreporting is Phase 2 of [Feature] Orchestrate graph creation through PD: new graphs are not consistently available across Server replicas ("Could not rebind [g]") #3137.prepareSchemaare not made transactional here; they belong to the Phase 3 PD creation state machine.Verification
EventHubTest: 21 tests passed on JDK 11, including synchronous dispatch, listener-failure counting, and snapshot mutation.hugegraph-apiand all required reactor modules compiled successfully on JDK 11.Does this PR potentially affect the following parts?
Documentation Status