fix(node): bound the host groups a node's inbounds may adopt

A node-reported host group is stored once per address and per adopted
inbound, so the rows and bytes a merge wrote were the product of three
values the node chooses, unbounded by the response size cap: a small
hosts/list answer could fill the master's disk.

A group whose serialized form exceeds 16 KiB is no longer adopted, and
a node's inbounds hold at most 2000 host rows, counted over what they
already hold, so repeated merges do not add more; the merge logs how
many groups it left out.
This commit is contained in:
MHSanaei
2026-11-01 01:05:48 +01:00
parent 3526a65f5d
commit f9f21fd8a4
3 changed files with 128 additions and 4 deletions
+22 -1
View File
@@ -654,6 +654,16 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
}
}
// The host rows the node's inbounds already hold count against this merge's adoption.
hostGroups := adoptableHostGroups(snap.HostGroups)
hostGroupsSkipped := len(snap.HostGroups) - len(hostGroups)
hostRowBudget := 0
if len(hostGroups) > 0 {
if hostRowBudget, err = adoptedHostRowBudget(db, nodeID); err != nil {
return false, err
}
}
tx := db.Begin()
committed := false
defer func() {
@@ -821,9 +831,13 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
if newIb.Tag != snapIb.Tag {
tagToCentral[newIb.Tag] = &newIb
}
if rows := adoptedHostRows(snap.HostGroups, snapIb.Id, newIb.Id); len(rows) > 0 {
rows, skipped := adoptedHostRows(hostGroups, snapIb.Id, newIb.Id, hostRowBudget)
hostGroupsSkipped += skipped
if len(rows) > 0 {
if err := tx.Create(&rows).Error; err != nil {
logger.Warningf("setRemoteTraffic: adopt host rows for tag %q failed: %v", newIb.Tag, err)
} else {
hostRowBudget -= len(rows)
}
}
newInboundIDs[newIb.Id] = struct{}{}
@@ -901,6 +915,13 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
}
}
if hostGroupsSkipped > 0 {
logger.Warningf(
"setRemoteTraffic: node %d: left out %d host group adoption(s) — a group is limited to %d bytes and the node's inbounds to %d host rows",
nodeID, hostGroupsSkipped, maxAdoptedHostGroupBytes, maxAdoptedHostRows,
)
}
// The inbound each reported email now lives under, so a swept inbound's
// accumulator rows follow the email instead of being dropped and re-seeded at 0.
snapEmailHome := make(map[string]int, len(snapEmailsAll))