This commit is contained in:
@@ -22,6 +22,14 @@ import (
|
||||
"git.send.nrw/sendnrw/decent-webui/internal/mesh"
|
||||
)
|
||||
|
||||
func parseDuration(s string, def time.Duration) time.Duration {
|
||||
d, err := time.ParseDuration(strings.TrimSpace(s))
|
||||
if err != nil || d <= 0 {
|
||||
return def
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
/*** Config ***/
|
||||
func loadConfig() AppConfig {
|
||||
// HTTP
|
||||
@@ -44,6 +52,9 @@ func loadConfig() AppConfig {
|
||||
DiscoveryAddress: getenvDefault("MESH_DISCOVERY_ADDR", "239.8.8.8:9898"),
|
||||
}
|
||||
|
||||
m.PeerTTL = parseDuration(os.Getenv("MESH_PEER_TTL"), 2*time.Minute)
|
||||
m.PruneInterval = parseDuration(os.Getenv("MESH_PRUNE_INTERVAL"), 30*time.Second)
|
||||
|
||||
// Wenn keine AdvertURL gesetzt ist, versuche eine sinnvolle Herleitung:
|
||||
if strings.TrimSpace(m.AdvertURL) == "" {
|
||||
m.AdvertURL = inferAdvertURL(m.BindAddr)
|
||||
@@ -440,6 +451,10 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("mesh init: %v", err)
|
||||
}
|
||||
|
||||
// Hintergrund-Pruner starten
|
||||
mnode.StartPeerPruner()
|
||||
|
||||
go func() {
|
||||
log.Printf("[mesh] listening on %s advertise %s seeds=%v discovery=%v",
|
||||
cfg.Mesh.BindAddr, cfg.Mesh.AdvertURL, cfg.Mesh.Seeds, cfg.Mesh.EnableDiscovery)
|
||||
|
||||
@@ -30,7 +30,9 @@ type Config struct {
|
||||
Seeds []string // other peers' mesh base URLs
|
||||
ClusterSecret string // HMAC key
|
||||
EnableDiscovery bool
|
||||
DiscoveryAddress string // "239.8.8.8:9898"
|
||||
DiscoveryAddress string // "239.8.8.8:9898"
|
||||
PeerTTL time.Duration // wie lange darf ein Peer inaktiv sein (Default siehe unten)
|
||||
PruneInterval time.Duration // wie oft wird gepruned
|
||||
}
|
||||
|
||||
type Peer struct {
|
||||
@@ -72,6 +74,78 @@ type Node struct {
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// RemovePeer löscht einen Peer aus der Peer-Tabelle. Seeds werden standardmäßig nicht entfernt.
|
||||
func (n *Node) RemovePeer(url string) bool {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
if url == "" || url == n.self.URL {
|
||||
return false
|
||||
}
|
||||
// Seeds schützen
|
||||
if n.isSeed(url) {
|
||||
return false
|
||||
}
|
||||
if _, ok := n.peers[url]; ok {
|
||||
delete(n.peers, url)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PruneNow entfernt alle Peers, deren LastSeen vor cutoff liegt (Seeds bleiben).
|
||||
func (n *Node) PruneNow(cutoff time.Time) int {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
removed := 0
|
||||
for url, p := range n.peers {
|
||||
if url == n.self.URL || n.isSeed(url) {
|
||||
continue
|
||||
}
|
||||
if p.LastSeen.IsZero() || p.LastSeen.Before(cutoff) {
|
||||
delete(n.peers, url)
|
||||
removed++
|
||||
}
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
// StartPeerPruner startet den Hintergrundjob (stoppt automatisch bei n.stop).
|
||||
func (n *Node) StartPeerPruner() {
|
||||
go n.loopPrunePeers()
|
||||
}
|
||||
|
||||
func (n *Node) loopPrunePeers() {
|
||||
ttl := n.cfg.PeerTTL
|
||||
if ttl <= 0 {
|
||||
ttl = 2 * time.Minute
|
||||
}
|
||||
interval := n.cfg.PruneInterval
|
||||
if interval <= 0 {
|
||||
interval = 30 * time.Second
|
||||
}
|
||||
t := time.NewTicker(interval)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-n.stop:
|
||||
return
|
||||
case <-t.C:
|
||||
cutoff := time.Now().Add(-ttl)
|
||||
_ = n.PruneNow(cutoff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// helper: ist url ein Seed?
|
||||
func (n *Node) isSeed(url string) bool {
|
||||
for _, s := range n.cfg.Seeds {
|
||||
if strings.TrimSpace(s) == strings.TrimSpace(url) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func New(cfg Config, cbs Callbacks) (*Node, error) {
|
||||
if cfg.BindAddr == "" || cfg.AdvertURL == "" {
|
||||
return nil, errors.New("mesh: BindAddr and AdvertURL required")
|
||||
|
||||
Reference in New Issue
Block a user