feat(inbounds): improve multi-node online attribution (#6164)

This commit is contained in:
isultanov99
2026-08-15 17:40:35 +02:00
committed by GitHub
parent 2d669fa4b7
commit be70535b94
5 changed files with 399 additions and 21 deletions
+66 -5
View File
@@ -176,7 +176,12 @@ type process struct {
// mutex guards this map, onlineClients, and localLastOnline above so the
// online getters never see a torn read.
nodeOnlineTrees map[int]map[string][]string
onlineMu sync.RWMutex
// nodeActiveInboundTrees mirrors nodeOnlineTrees for active inbound tags:
// each direct node reports a GUID-keyed subtree of inbound tags that carried
// traffic within its own grace window. The inbounds page combines this with
// nodeOnlineTrees so a multi-inbound client is not shown on an idle inbound.
nodeActiveInboundTrees map[int]map[string][]string
onlineMu sync.RWMutex
// onlineAPISupport caches whether the running core implements the
// online-stats RPCs (GetUsersStats). A new process is created on every
@@ -398,10 +403,9 @@ func (p *Process) GetMergedNodeTrees() map[string][]string {
}
// GetLocalActiveInbounds returns a copy of THIS panel's inbound tags that
// carried traffic within the grace window. Only the local xray reports
// per-inbound activity; remote-node snapshots don't carry it, so the service
// layer keys these under the panel's own GUID and a node missing from the
// active-inbounds map means "don't gate" (fall back to the email-only signal).
// carried traffic within the grace window. The service layer keys these under
// the panel's own GUID before merging them with remote-node active-inbound
// subtrees.
func (p *Process) GetLocalActiveInbounds() []string {
p.onlineMu.RLock()
defer p.onlineMu.RUnlock()
@@ -413,6 +417,43 @@ func (p *Process) GetLocalActiveInbounds() []string {
return out
}
// GetMergedActiveInboundTrees returns the union of every direct node's reported
// active-inbound subtree, keyed by the panelGuid of the node that physically
// hosts each inbound. Duplicate tags reported through multiple paths are
// deduped per GUID.
func (p *Process) GetMergedActiveInboundTrees() map[string][]string {
p.onlineMu.RLock()
defer p.onlineMu.RUnlock()
if len(p.nodeActiveInboundTrees) == 0 {
return map[string][]string{}
}
out := make(map[string][]string)
seen := make(map[string]map[string]struct{})
for _, tree := range p.nodeActiveInboundTrees {
for guid, tags := range tree {
if guid == "" || len(tags) == 0 {
continue
}
dedup := seen[guid]
if dedup == nil {
dedup = make(map[string]struct{}, len(tags))
seen[guid] = dedup
}
for _, tag := range tags {
if tag == "" {
continue
}
if _, ok := dedup[tag]; ok {
continue
}
dedup[tag] = struct{}{}
out[guid] = append(out[guid], tag)
}
}
}
return out
}
// RefreshLocalOnline records that each email in activeEmails and each tag in
// activeInboundTags had local xray traffic at now, then rebuilds onlineClients
// and localActiveInbounds from every entry seen within graceMs, pruning older
@@ -462,12 +503,31 @@ func (p *Process) RefreshLocalOnline(activeEmails, activeInboundTags []string, n
func (p *Process) SetNodeOnlineTree(nodeID int, tree map[string][]string) {
p.onlineMu.Lock()
defer p.onlineMu.Unlock()
if len(tree) == 0 {
delete(p.nodeOnlineTrees, nodeID)
return
}
if p.nodeOnlineTrees == nil {
p.nodeOnlineTrees = map[int]map[string][]string{}
}
p.nodeOnlineTrees[nodeID] = tree
}
// SetNodeActiveInboundTree records the GUID-keyed active-inbound subtree one
// direct remote node reported. Replaces any previous entry for that node.
func (p *Process) SetNodeActiveInboundTree(nodeID int, tree map[string][]string) {
p.onlineMu.Lock()
defer p.onlineMu.Unlock()
if len(tree) == 0 {
delete(p.nodeActiveInboundTrees, nodeID)
return
}
if p.nodeActiveInboundTrees == nil {
p.nodeActiveInboundTrees = map[int]map[string][]string{}
}
p.nodeActiveInboundTrees[nodeID] = tree
}
// ClearNodeOnlineClients drops a direct node's whole subtree contribution.
// Called when a probe fails so a downed node — and everything behind it — doesn't
// keep its clients listed as "online" until the next successful probe.
@@ -475,6 +535,7 @@ func (p *Process) ClearNodeOnlineClients(nodeID int) {
p.onlineMu.Lock()
defer p.onlineMu.Unlock()
delete(p.nodeOnlineTrees, nodeID)
delete(p.nodeActiveInboundTrees, nodeID)
}
// GetUptime returns the uptime of the Xray process in seconds.