fix(pd): keep KvClient watches alive after reconnect failures - #3157
Conversation
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: The reconnect implementation has critical identity and failure-propagation risks that can silently stop watches and invalidate existing locks; permanent callback errors also retry without termination. Evidence: static review of KvClient.java and AbstractClient.java at head 772b3a5, with exact server lock/watch handling cross-checked.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #3157 +/- ##
============================================
+ Coverage 32.68% 41.35% +8.66%
- Complexity 5516 7294 +1778
============================================
Files 789 802 +13
Lines 67750 69659 +1909
Branches 8953 9285 +332
============================================
+ Hits 22147 28805 +6658
+ Misses 42990 37569 -5421
- Partials 2613 3285 +672 ☔ 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 current head addresses the previously reported watch reconnect, lock identity, terminal-error retry, and stale-observer issues; focused validation passes, but coverage checks remain failed and independent review evidence is incomplete. Evidence: mvn -pl hugegraph-pd/hg-pd-test -am -Dtest=KvClientTest -DfailIfNoTests=false test (18/18 passed), git diff --check passed; codecov/project and codecov/patch failed.
bitflicker64
left a comment
There was a problem hiding this comment.
Blocking: no. Summary: Splitting the lock and watch client IDs, tracking each watch as an independent subscription, and propagating the terminal streaming failure are all solid, and the regression suite is genuinely deterministic; two gaps remain in the reconnect loop itself, plus one visibility nit. Evidence: read of KvClient.java, AbstractClient.java and KvClientTest.java at 3c2f4df; repo greps for streamingCall/listen callers; PD server watch path in KvWatchSubject.notifyClientChangeLeader and KvServiceGrpcImpl.clientWatch; gh -R apache/hugegraph pr checks 3157 (17 pass, only codecov/patch and codecov/project fail).
There was a problem hiding this comment.
Pull request overview
This PR fixes PD KvClient watch reliability by ensuring watches continue to reconnect after repeated failures, leader changes, and unexpected stream completion, while also addressing a lifecycle bug where transport resets could inadvertently invoke subclass close() behavior.
Changes:
- Refactors
AbstractClientto separate transport cleanup (closeConnections()) from the overridableclose()lifecycle. - Reworks
KvClientwatch handling to track independent watch subscriptions and keep reconnecting with deduped scheduled retries until recovery or client close. - Adds regression tests covering repeated failures/recovery, leader changes, completion recovery, deduplication, stale observers, prefix semantics, and close behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/AbstractClient.java | Avoids invoking subclass lifecycle close() during transport reset; improves streaming-call retry failure signaling. |
| hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/KvClient.java | Introduces per-watch subscription tracking and scheduled reconnect loop to prevent watches permanently stopping. |
| hugegraph-pd/hg-pd-test/src/main/java/org/apache/hugegraph/pd/client/KvClientTest.java | Adds deterministic coverage for reconnect behaviors and the transport initialization lifecycle regression. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- merge the latest apache master changes - preserve debug-level watch event logging - keep the dedicated watch client identifier - retain the reconnect lifecycle implementation
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. The latest update fixes the reconnect executor parking reported in comment 3885719895, and the focused KvClientTest passes 19/19, but four current-head races can still silently strand watches. The hstore check is red on VertexCoreTest.testQueryByJointIndexesWithSearchAndTwoRangeIndexesAndWithin; exact base 7bb624b fails the same assertion, so it appears baseline-related, but visible CI remains a merge gate.
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. The focused KvClientTest passes 19/19, but four current-head lifecycle and failure-handling gaps can still strand watches. The hstore check is red on VertexCoreTest.testQueryByJointIndexesWithSearchAndTwoRangeIndexesAndWithin; exact base 7bb624b fails the same assertion, so it appears baseline-related, but visible CI remains a merge gate.
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: the latest head fixes several earlier global-state and lifecycle issues, but four production correctness gaps remain. Evidence: exact head 4202f26, CI is green, and the focused tests pass 33/33; a separate design comment proposes consolidating the watch state machine instead of adding more guards.
imbajin
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: The single-thread reconnect scheduler can serialize all watches behind a 60-second peer-discovery attempt, delaying recovery and close. Evidence: exact head 0a77c55; PDConfig defaults grpcTimeOut to 60000 ms; reconnectExecutor is single-threaded; startWatch synchronously reaches AbstractClient.resetStub() and blocking getMembers().
bitflicker64
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: The AbstractClient transport/lifecycle split and the per-subscription WatchSubscription model are the right fixes, and the new watch tests genuinely run and pass, but the SchemaDriverTest added here fails CI at this head, and two new lifecycle paths hold a monitor across an unbounded channel shutdown loop. Evidence: exact head 0a77c55ac6fa0baaf0e97d98eb0d15a1c28a1531 read via gh api -X GET repos/apache/hugegraph/pulls/3157/files --paginate and gh api -X GET "repos/apache/hugegraph/contents/<path>?ref=0a77c55a" -H 'Accept: application/vnd.github.v3.raw'; CI via gh api -X GET repos/apache/hugegraph/commits/0a77c55a/check-runs (22 success, 2 failure) with logs from gh api -X GET repos/apache/hugegraph/actions/jobs/99501401894/logs; PDClientSuiteTest runs 45 tests on #3164 and 77 here, matching the 3 to 35 growth in KvClientTest.
Three smaller notes, not worth inline comments. The description says -Dtest=KvClientTest ran "12 tests, 0 failures/errors", but this head has 35 @Test methods in that class, so that figure looks like it came from the first of the six commits on the branch. "Main Changes" does not mention the hugegraph-struct and SchemaDriver work, which is two of the five changed files. And the hstore failure on VertexCoreTest.testQueryByJointIndexesWithSearchAndTwoRangeIndexesAndWithin is joint and range index behaviour with no path to the PD watch client or SchemaDriver, and that job passes on #3164, so I do not attribute it to this PR, though I did not reproduce it.
I did not restate the review comments already open at this head on watch readiness, unconditional reconnect, shouldRotateWatchTransport coverage, semaphore ownership, WatchRequest placement, stopped watch visibility, or the stale Started test gap. The three below are distinct from those. No local build backs these findings: fetching the head into a checkout was not available in this session, so this is static review of the exact head sources plus CI logs.
bitflicker64
left a comment
There was a problem hiding this comment.
Blocking: yes. Summary: the per-subscription watch model, the AbstractClient transport/lifecycle split and the 40-test regression suite are the right fixes for #3152 and all pass locally, but KvClient.stubResetTimeoutMillis() applies the 5 s watch-start budget to every blocking KV and lock call, cutting the honoured pd.grpc_timeout from a measured 21.3 s to a pinned 5.0 s on the same peers; SchemaDriver.destroy() also publishes the driver while its KvClient is already closed. Evidence: exact head f99b6bd vs merge base 7bb624b; mvn -o -pl hugegraph-pd/hg-pd-test -am test -Dtest=KvClientTest 40/40 and mvn -o -pl hugegraph-struct -am test -Dtest=SchemaDriverTest 5/5 pass; upstream CI green 24/24; timing probe over three unreachable peers with identical PDConfig gives KvClient.get() 4984/5213/5212 ms at head against 21328/35403/6511 ms at base, while MetaClient.getGraphs() on the same peers stays at 25071 ms.
|
Live before/after for this branch (head Observable that lazy loading cannot compensate: in PD-meta mode
B's log on this branch during each outage: So the failure described in #3152 reproduces on master with a plain 30 s PD restart, and this branch recovers every metadata watch after it, twice in a row. Two small remarks, neither blocking:
Scripts and full logs (both sides, both variants): https://github.com/SebastianGruza/hugegraph-oracle-suite — |
- limit the watch discovery budget to asynchronous stubs - unpublish SchemaDriver before closing owned resources - cover configured blocking deadlines and destroy visibility
imbajin
left a comment
There was a problem hiding this comment.
Pushed eeb7041 to this branch to fix the two confirmed P1 issues: blocking KV/lock discovery keeps its configured timeout, and SchemaDriver is unpublished before resource cleanup while concurrent init remains rejected. Both regression checks failed before the fix and pass now.
Five independent review lanes, including an adversarial review, found no remaining P0/P1 blocker after the fixes; the final incremental diff was independently checked. Validation: KvClientTest 41/41, SchemaDriverTest 5/5, editorconfig:format, full clean compile and diff check passed. Review score: 8.5/10; merge readiness still depends on CI for this new head.
The legacy four-argument streamingCall override concern in discussion_r3901065273 remains open as a compatibility follow-up. There is no production override in this repository; replacing both overloads with a private delegate would bypass the existing override dispatch, so I left that contract change for discussion. This PR restores watch reconnection; missed-event reconciliation remains tracked separately in #3151.
bitflicker64
left a comment
There was a problem hiding this comment.
Blocking: no. Summary: the AbstractClient transport/lifecycle split and the per-subscription watch model are the right fix for #3152, CI is green at this head, and the earlier P0/P1 reports are addressed; three regressions introduced by the hardening itself remain open (two in the new SchemaDriver lifecycle, one in AbstractClient locking) plus one piece of dead protected API. Evidence: full exact-head diff at eeb7041 compared against origin/master baselines of AbstractClient, KvClient and SchemaDriver; call-site reads of SchemaGraph.schemaDriverInit(), PdMetaDriver, AbstractClientStubProxy and PDException; gh pr checks 3157 reports 24/24 green.
- remove the unused unconditional async stub invalidator - document watch recovery and terminal error callbacks - clarify timeout and event replay boundaries
imbajin
left a comment
There was a problem hiding this comment.
+1. Score: 9/10 for the agreed watch-recovery scope. Five independent review lanes, including an adversarial review, and final whole-diff verification are complete. KvClientTest (41) and SchemaDriverTest (5), formatting, full clean compile and all 22 current-head CI checks pass. No remaining blockers within this scope.
Accepted boundaries: shared-transport recovery may briefly delay ordinary KV/lock requests; callers coordinate SchemaDriver initialization and destruction; existing streaming override dispatch is preserved. The remaining threads are resolved on these explicit scope decisions, not on a claim that the tradeoffs were eliminated.
Next: #3151 for missed-event graph metadata reconciliation and discussion #3205 for schema cache reconciliation. This PR restores future notifications for #3152; it does not replay missed events or resolve those convergence workstreams.
Brings in apache#3220 and apache#3157; no file overlaps with the chart branch.
Purpose of the PR
KvClientpreviously retried a failed watch only once. If that reconnect also failed, thewatch stopped permanently, and unexpected stream completion did not trigger recovery.
The reconnect path also exposed a lifecycle issue:
AbstractClient.resetStub()invoked thevirtual
close()method, so transport initialization on aKvClientdispatched toKvClient.close()and marked the whole client closed.Reconnect Lifecycle
Main Changes
AbstractClienttransport cleanup from the overridable client lifecycle close.onError,Leader_Changed, and unexpectedonCompletedwith a fixed delay until thewatch recovers or the client closes.
KvClient.close()is called.changes, completion, deduplication, stale observers, prefix semantics, and close behavior.
This PR does not add event replay, protocol revisions, PD Server changes, or the Server-side
reconciliation tracked by #3151. Events emitted while a watch is disconnected are still not
replayed.
Verifying these changes
mvn -q -o test -pl hugegraph-pd/hg-pd-test -am -Dtest=KvClientTest -DfailIfNoTests=false -Drat.skip=true -Djacoco.skip=true(12 tests, 0 failures/errors)mvn -q -o package -pl hugegraph-pd -am -DskipTests -Dmaven.javadoc.skip=true -Drat.skip=true -Djacoco.skip=truemvn -q -o -f hugegraph-pd/hg-pd-client/pom.xml apache-rat:checkmvn -q -o -f hugegraph-pd/hg-pd-test/pom.xml apache-rat:checkThe full local PD suite was also attempted. The common suite passed 83/83 and the core suite
passed 89/91 with 2 skipped. The client suite could not complete without a local PD service at
127.0.0.1:8686(4 failures and 5 errors reported connection refused), which prevented the restsuite from executing in that Maven run.
Repository-wide RAT is not a valid signal in this checkout because pre-existing ignored
.upgrade-artifactsfiles produce 68 unrelated unapproved-license entries; RAT passes for bothchanged modules.
Does this PR potentially affect the following parts?
Documentation Status
Doc - TODODoc - DoneDoc - No NeedVisual summary