mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 15:47:14 +00:00
fix(node): push a node only the client IPs it hosts
A master's per-node sync must scope what it sends to the clients that node serves, so its cost tracks the node and not the fleet. The global-usage push already did (node_client_traffics by node_id); the 10s client-IP push sent GetAllInboundClientIps, the whole table, to every node. Each node's MergeInboundClientIps then created a row for every foreign email, and its next GET clientIps echoed the whole fleet back. Its IP-limit job only ever reads rows for its own clients, so none of it was used. With 150 nodes x 150 clients, one IP tick pushed 299 MB and pulled 264 MB, every node held 22,500 rows instead of 150, and sync ticks grew 3.8s -> 10.2s even at 1ms latency; the cost grows with the square of the fleet. Both pushes now share nodeHostedEmails. After the change the same fleet moves 2.0 MB / 1.8 MB per tick and ticks stay near 3.2s. Nodes upgraded with foreign rows shed them within 30 minutes via pruneStaleIpRows.
This commit is contained in:
@@ -19,6 +19,33 @@ func (s *InboundService) GetAllInboundClientIps() ([]model.InboundClientIps, err
|
||||
return ips, err
|
||||
}
|
||||
|
||||
// nodeHostedEmails is every client one node serves, its descendants' included.
|
||||
// Per-node pushes are scoped to it so their cost tracks the node, not the fleet.
|
||||
func nodeHostedEmails(db *gorm.DB, nodeID int) ([]string, error) {
|
||||
var emails []string
|
||||
err := db.Model(&model.NodeClientTraffic{}).Where("node_id = ?", nodeID).Pluck("email", &emails).Error
|
||||
return emails, err
|
||||
}
|
||||
|
||||
// GetNodeInboundClientIps returns the IP rows of the clients nodeID hosts: a node's
|
||||
// IP-limit job reads no other row, so pushing the rest only made it echo them back.
|
||||
func (s *InboundService) GetNodeInboundClientIps(nodeID int) ([]model.InboundClientIps, error) {
|
||||
db := database.GetDB()
|
||||
emails, err := nodeHostedEmails(db, nodeID)
|
||||
if err != nil || len(emails) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
var ips []model.InboundClientIps
|
||||
for _, batch := range chunkStrings(emails, sqlInChunk) {
|
||||
var page []model.InboundClientIps
|
||||
if err := db.Where("client_email IN ?", batch).Find(&page).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ips = append(ips, page...)
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
// clientIpStaleAfterSeconds mirrors job.ipStaleAfterSeconds: client IPs older than
|
||||
// 30 minutes are evicted. Applying the same cutoff inside the cross-node merge keeps
|
||||
// the synced blob bounded and stops the master's push-back from resurrecting IPs that
|
||||
|
||||
Reference in New Issue
Block a user