// 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 // RevokeFn cancels the forward on this node (stop worker + drop from store). RevokeFn 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) } // Revoke implements Handler. func (a *AppHandler) Revoke(ctx context.Context, tk *Task) error { if a.RevokeFn == nil { return nil } return a.RevokeFn(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 }