Repository navigation
mcpx: recover resumed HTTP sessions and clean removed servers (#1416) #5658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,8 @@ func (r *Registry) Reload(cfg *config.Config) (added, removed []string, err erro | |
| return nil, nil, err | ||
| } | ||
|
|
||
| r.reloadMu.Lock() | ||
| defer r.reloadMu.Unlock() | ||
| r.mu.Lock() | ||
| defer r.mu.Unlock() | ||
|
|
||
|
|
@@ -84,26 +86,74 @@ func (r *Registry) Reload(cfg *config.Config) (added, removed []string, err erro | |
| keep[p] = true | ||
| } | ||
| var orphans []*pool.Pool | ||
| removedPools := map[*pool.Pool]bool{} | ||
| for name, p := range r.pools { | ||
| if _, still := pools[name]; !still { | ||
| removed = append(removed, name) | ||
| removedPools[p] = true | ||
| } | ||
| if !keep[p] { | ||
| orphans = append(orphans, p) | ||
| } | ||
| } | ||
| sort.Strings(removed) | ||
|
|
||
| r.cfg = cfg | ||
| r.pools, r.views, r.order = pools, views, order | ||
| r.hash = HashConfig(mustJSON(cfg.MCPServers)) | ||
| // Mark orphans closed before the replacement map is visible. A later | ||
| // reload must not resume a record whose old client is still draining and | ||
| // will DELETE that upstream session. | ||
| for _, p := range orphans { | ||
| p.CloseDrained() | ||
| } | ||
| if r.pendingSessionCleanup == nil { | ||
| r.pendingSessionCleanup = map[string]int{} | ||
| } | ||
| for p := range removedPools { | ||
| r.pendingSessionCleanup[p.Name()]++ | ||
| } | ||
|
|
||
| // Drained, not closed: a call running on an orphan finishes on it, as it | ||
| // does on a lazy restart. Close would cut one that ran past its grace. | ||
| go func() { | ||
| for _, p := range orphans { | ||
| p.CloseDrained() | ||
| p.WaitDrained() | ||
| if removedPools[p] { | ||
| r.reloadMu.Lock() | ||
| r.mu.RLock() | ||
| _, reconfigured := r.pools[p.Name()] | ||
| r.mu.RUnlock() | ||
| if !reconfigured { | ||
| p.CleanupRemovedSessionRecords() | ||
| } | ||
| if r.pendingSessionCleanup[p.Name()] <= 1 { | ||
| delete(r.pendingSessionCleanup, p.Name()) | ||
| } else { | ||
| r.pendingSessionCleanup[p.Name()]-- | ||
| } | ||
| r.reloadMu.Unlock() | ||
| } | ||
| } | ||
| r.reloadMu.Lock() | ||
| r.mu.RLock() | ||
| configured := make(map[string]bool, len(r.pools)) | ||
| for name := range r.pools { | ||
| configured[name] = true | ||
|
Comment on lines
+139
to
+141
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| // A later reload can have no orphan pools while an earlier removed | ||
| // pool is still draining. Keep those records out of the generic | ||
| // discard path until the owner has attempted DELETE with its last | ||
| // live config. | ||
| for name := range r.pendingSessionCleanup { | ||
| configured[name] = true | ||
| } | ||
| var sessions pool.SessionStore | ||
| if r.hooks != nil { | ||
| sessions = r.hooks.Sessions | ||
| } | ||
| r.mu.RUnlock() | ||
| pool.DiscardUnconfiguredSessionRecords(sessions, configured) | ||
|
dezren39 marked this conversation as resolved.
Resolved
|
||
| r.reloadMu.Unlock() | ||
| }() | ||
| return added, removed, nil | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: developing-today/code
Length of output: 4325
Script executed:
Repository: developing-today/code
Length of output: 9692
Script executed:
Repository: developing-today/code
Length of output: 4801
Script executed:
Repository: developing-today/code
Length of output: 1646
Script executed:
Repository: developing-today/code
Length of output: 8886
Script executed:
Repository: developing-today/code
Length of output: 3684
Script executed:
Repository: developing-today/code
Length of output: 2935
Move the upstream DELETEs out of the
reloadMusection.The cleanup goroutine calls
CleanupRemovedSessionRecordswhile holdingreloadMu. Each matching record can block for up toupstream.sessionDeleteTimeout(5s), and later reloads wait for all of them. Run the DELETEs outsidereloadMu, and keep the re-add check race-safe.