feat(M6): token ring data plane + engine — pending adds disappear on claim, topology written back for full cluster view (per authoritative design)

This commit is contained in:
2026-08-18 08:24:37 +08:00
parent 5bd70b90c0
commit f6c4b91a96
9 changed files with 1166 additions and 202 deletions

View File

@ -0,0 +1,61 @@
// Runtime handlers the ring engine needs, provided by the app:
// - RuntimeLoad: sample this node's memory/network load;
// - Claim: create the frpc worker/forward for a claimed task.
package cluster
import (
"context"
"os"
"runtime"
)
// AppHandler is the app-provided bridge for ring load sampling + task claims.
type AppHandler struct {
// LoadFn returns (mem%, net%) — lower is more idle.
LoadFn func() (memPct, netPct float64)
// ClaimFn creates the forward on this node (persist + spawn worker).
ClaimFn func(ctx context.Context, tk *Task) error
}
// RuntimeLoad implements Handler.
func (a *AppHandler) RuntimeLoad() Load {
mem, net := 0.0, 0.0
if a.LoadFn != nil {
mem, net = a.LoadFn()
}
return Load{MemPct: mem, NetPct: net}
}
// Claim implements Handler.
func (a *AppHandler) Claim(ctx context.Context, tk *Task) error {
if a.ClaimFn == nil {
return nil
}
return a.ClaimFn(ctx, tk)
}
// SampleMemLoad returns a cheap memory-usage percentage (0..100).
func SampleMemLoad() float64 {
var m runtime.MemStats
runtime.ReadMemStats(&m)
total := m.Sys
if total == 0 {
return 0
}
return float64(m.Alloc) / float64(total) * 100
}
// SampleNetLoad is an approximation: bytes in/out relative to a soft budget.
// Kept simple; production could use /proc/net/dev deltas.
func SampleNetLoad() float64 {
return 0
}
// HostID returns a stable node id (hostname; empty fallback to pid).
func HostID() string {
h, _ := os.Hostname()
if h == "" {
return "node"
}
return h
}