Repository navigation
Conversation
|
Hi @Aias00 ,
|
Aias00
left a comment
There was a problem hiding this comment.
LGTM on the isolation itself, and the snapshot copy is a better answer than I expected. One coordination issue and two nits.
What I verified
copyInstanceInfoVOcovers all nine fields ofInstanceInfoVO(iddoes not exist on that VO), andInstanceInfoServiceImpl.createOrUpdatederives its lookup from ip/port/type/namespace and itsupdateByIdfrom the DO it just fetched — it never reads an id off the VO. So the copy is safe for both the insert and the update path, and it closes the window wheredoCheckmutatesinstanceStatein the middle of the JDBC call.- The new test actually proves the bug: it blocks
createOrUpdate, then invokesdoCheckon the calling thread and requires the entry to reach OFFLINE while the sync is still parked. On master both tasks share one thread, so that assertion is precisely what would fail. close()now shuts both executors down before the final sync and clears the map infinally, so a failing final sync no longer leaks the cached entries.
Coordination issue (please address)
#7765, same author, rewrites this same syncDB method (it adds a per-instance try/catch) and is already approved. The two will conflict textually, and the merged result should keep #7765's per-instance granularity: with a whole-loop catch here, one failing row aborts the remainder of the sync, which is the opposite of what #7765 fixes. Please rebase one on the other and say in the PR which granularity wins.
Nits
synchronized syncDB()plusclose()callingsyncDB()means@PreDestroyblocks for the duration of any in-flight sync; with a hung JDBC call that is an unbounded shutdown delay. A guardedtryLockor a timeout would bound it.setup()still allocates fresh executors on every call, so a second call (cluster re-election) leaks the previous pair. #7765 closing on master loss covers this in practice, but an explicit close-if-present at the top ofsetup()would make it airtight.
CI: no workflow run on the branch. Please make sure pr_build is green before merge.
c314d64 to
dd14332
Compare
|
Thanks for the detailed review @Aias00! I've addressed all the feedback and rebased the branch onto the latest
|
|
@harshkha3417 PTAL. Heads-up for after #7742 (approved — adds |
Aias00
left a comment
There was a problem hiding this comment.
Approved.
Good isolation fix for #7744. Splitting the DB sync into its own single-thread executor means a slow or hung syncDB can no longer block the heartbeat liveness check that shared the original executor. The ReentrantLock (with tryLock and a timed variant) prevents overlapping syncs, doSyncDB() snapshots and copies each VO to decouple the DB layer from concurrent cache mutations, and setup() now closes stale executors on re-election to avoid leaks. close() shuts both executors and performs a bounded final sync. Tests cover executor isolation, re-election cleanup, and the non-blocking timed-lock path.
Minor note: CI checks have not reported on this branch yet; please confirm the workflow passes before merging.
dd14332 to
489b798
Compare
|
Thanks for the heads-up @konlue! Understood — since #7742 hasn't landed on |
|
All CI checks have passed successfully. The PR is ready for merge whenever you have a moment, @Aias00. Thank you! |
489b798 to
f9befac
Compare
Aias00
left a comment
There was a problem hiding this comment.
Re-approving after I rebased this branch onto current master (the force-push dismissed the previous approval).
What I did:
- Rebased the 3 commits onto master (de93952) as the maintainer, since #7785 and #7721 had touched the same file and left the PR CONFLICTING.
- The only conflict was a single import block in
InstanceCheckServiceTest.java: master addedorg.apache.shenyu.common.utils.GsonUtils, this PR addedorg.apache.shenyu.common.enums.InstanceStatusEnum. Both are used (1 and 2 references respectively), so I kept both in checkstyle alphabetical order. InstanceCheckService.javaauto-merged cleanly. I confirmed the two sides are disjoint: master changesneedlessHeartBeat/metadata refresh only, this PR changessetup,syncDB/doSyncDB,closeand adds thesyncExecutor/syncLockfields.- Verified no conflict markers remain, authorship preserved on all three commits, and the diff versus master is unchanged in substance (81 lines in
InstanceCheckService.java, 158 in the test).
The original review stands: splitting the DB sync into its own single-thread executor so a slow syncDB cannot block the heartbeat liveness check, ReentrantLock with tryLock to prevent overlapping syncs, snapshot-and-copy in doSyncDB, and setup() closing stale executors on re-election to avoid leaks. Functional review unaffected by the rebase.
CI Failure Root Cause & ResolutionThe failure in the CI build and prbuild checks was caused by an upstream conflict between two recently merged commits on
|
|
@Aias00 CI run approval is needed... |
Aias00
left a comment
There was a problem hiding this comment.
Re-reviewed at 0e6bef7b. The scheduler isolation is the right fix for #7744 and the implementation is sound. One coordination problem needs your attention before this can merge.
What I verified:
syncExecutoris now a separate single-threadScheduledThreadPoolExecutor, so a slowinstanceInfoService.createOrUpdatecan no longer delay the heartbeat sweep. That is exactly the issue.setup()callscloseExecutors()first, so a secondsetup()(cluster mode re-election after losing leadership) does not leak the previous pair of executors.shutdown()withoutawaitTerminationis acceptable here because both pools are single-threaded and the tasks are short.syncDB(timeout, unit)guards re-entry withReentrantLock#tryLockand only unlocks when the lock was actually acquired — thelockedflag makes thefinallycorrect onInterruptedException.doSyncDB()iterates a snapshot and handscopyInstanceInfoVO(vo)to the service, so a heartbeat arriving mid-sync cannot mutate the object being persisted. Good call, this was a real race.- Per-entry
try/catchkeeps one bad instance from aborting the whole sweep. close()closes the executors before the final flush, so no new sweep can start while the last one runs, andinstanceHealthBeatInfo.clear()is in afinally.
Non-blocking notes:
copyInstanceInfoVO()hand-copies nine fields. Any field added toInstanceInfoVOlater will be silently dropped from persistence. A copy constructor onInstanceInfoVO, or aBeanUtils.copyPropertieswith an explicit ignore list, would fail loudly instead.close()relies on the lock with a 5s budget; if the periodic sweep is stuck in a slow DB call, the flush is skipped and the exception is swallowed bydoSyncDB().LOG.warnwhensyncDB(5, SECONDS)returnsfalseduring shutdown would make that visible.
Conflict with #7765
#7765 touches the same InstanceCheckService lines: the handleBeatInfo normalize call and a simplified syncDB() that wraps each createOrUpdate in try/catch but has no lock, no snapshot and no separate executor. Whichever lands second will conflict, and if #7765 lands second it would partially revert this design. Please coordinate so this PR is the one that owns syncDB(), and #7765 keeps only the scheduler-hardening part.
CI
There is no workflow run for this branch: GET /repos/apache/shenyu/actions/runs?head_sha=0e6bef7b returns total_count: 0 and gh pr checks reports nothing. Same for your other open PRs, so Actions appears to be disabled on your fork. Please enable it and push again — without a pr_build run this cannot pass the merge gate.
0e6bef7 to
11e99e3
Compare
|
Thanks @Aias00 for the review! I have addressed all review comments in this PR (which owns
|
|
/retest |
Aias00
left a comment
There was a problem hiding this comment.
LGTM. Splitting the db sync onto its own executor is the right fix — scheduleWithFixedDelay on a single-thread pool meant a slow createOrUpdate stalled the liveness check, which is exactly the false-offline path this PR targets.
Verified:
syncDB(timeout, unit)returns the lock state anddoSyncDBswallows per-row failures so one bad instance no longer aborts the whole cycle.close()now shuts the executors down first and bounds the final sync with a 5stryLock, so shutdown cannot hang on a hung DB write.setup()closing stale executors before recreating them fixes the re-election leak.- Copying the VO before
createOrUpdateavoids handing a live map entry to the persistence layer. InstanceInfoUtils.normalizeexists on master and the imports are all present.
Nit: the per-cycle copyInstanceInfoVO allocation is O(instances) every 10s. Fine for now, but a reuse buffer would avoid the churn.
|
Approved, but the checks are red on a shared e2e case rather than on this change.
The same two cases are red on every other PR that touches this module right now, so this is the shared e2e case rather than your change. Could you keep this rebased and re-run once the http e2e case is fixed upstream? Nothing else blocks my approval. |
|
/retest |
…n if final sync fails
…bound shutdown lock
…align test payloads
… fields in copyInstanceInfoVO
11e99e3 to
741e70e
Compare
What is the purpose of the change
Fixes #7744.
InstanceCheckServicepreviously scheduled both the periodic in-memory liveness checker andsyncDBon a single-threaded executor. Slow or blocked database access during synchronization caused the liveness check to be starved, keeping timed-out instances marked as ONLINE in memory.Brief change log
syncExecutoronto a dedicated single-threaded scheduler so database synchronization cannot block in-memory heartbeat checks.syncDB, wrapped it in try-catch error handling, and used point-in-time snapshot copies for database persistence.close()to shut down both executors cleanly.InstanceCheckServiceTestverifying that a blocked database synchronization does not prevent the liveness checker from updating states on time.Verifying this change
mvn checkstyle:check -pl shenyu-adminpasses with 0 violations.mvn test -pl shenyu-admin -Dtest=InstanceCheckServiceTestpasses cleanly.