fix: status + canvas pages now merge ring topology — all nodes see full cluster picture

Root cause: handleStatus built forwards only from local SQLite links table;
handleCanvasGet returned only local store data. Neither merged the ring
topology (which IS synced to every node in-memory). Result: non-claiming
nodes saw only their own claimed forwards, not the full cluster.

Fix: read-time merge in both handlers — iterate ring topology entries and
add any (local, remote, link) not already in the local store. No store
writes (avoids the per-forward stop/start issue that broke the old
topology-derived-canvas model).

Verified: all 4 cluster nodes now show 5 forwards / 4 remotes / 5 links;
isolated node-e correctly shows empty.
This commit is contained in:
2026-08-19 21:24:35 +08:00
parent c40d208b62
commit 465a86c755
2 changed files with 59 additions and 1 deletions

View File

@ -21,6 +21,43 @@ func (h *Handler) handleCanvasGet(w http.ResponseWriter, _ *http.Request) {
locals, _ := h.Store.ListLocals()
remotes, _ := h.Store.ListRemotes()
links, _ := h.Store.ListLinks()
// Merge ring topology entries not in the local store so every node's
// canvas shows the FULL cluster picture. This is a read-time merge —
// nothing is written back to SQLite (avoids the per-forward stop/start
// issue that broke the old topology-derived-canvas model).
if h.Ring != nil {
snap := h.Ring.Snapshot()
localNames := make(map[string]bool, len(locals))
for _, l := range locals {
localNames[l.Name] = true
}
remoteNames := make(map[string]bool, len(remotes))
for _, r := range remotes {
remoteNames[r.Name] = true
}
type linkKey struct{ local, remote string; port int }
linkSeen := make(map[linkKey]bool, len(links))
for _, l := range links {
linkSeen[linkKey{l.Local, l.Remote, l.RemotePort}] = true
}
for _, t := range snap.Topology {
if !localNames[t.Local.Name] {
locals = append(locals, t.Local)
localNames[t.Local.Name] = true
}
if !remoteNames[t.Remote.Name] {
remotes = append(remotes, t.Remote)
remoteNames[t.Remote.Name] = true
}
k := linkKey{t.Link.Local, t.Link.Remote, t.Link.RemotePort}
if !linkSeen[k] {
links = append(links, t.Link)
linkSeen[k] = true
}
}
}
writeJSON(w, http.StatusOK, canvasData{Locals: locals, Remotes: remotes, Links: links})
}