package pipeline import ( "hash/fnv" "time" "evobgp/internal/store" ) // SchedulerTickSec matches the scheduler ticker interval (internal/scheduler). const SchedulerTickSec = 30 // ModuleDueForScheduler reports whether a module's refresh interval bucket rolled since the last scheduler tick. // A stable per-module offset (fnv32 of ID) spreads bucket boundaries so modules with the same interval // do not all become due on the same tick (thundering herd). func ModuleDueForScheduler(mod *store.Module, now time.Time) bool { if mod == nil || !mod.Enabled || mod.Type == "IP_RANGES" || mod.RefreshIntervalSec <= 0 { return false } win := int64(mod.RefreshIntervalSec) if win < 60 { win = 60 } offset := moduleSchedulerOffset(mod.ID, win) cur := (now.Unix() - offset) / win prev := (now.Unix() - offset - SchedulerTickSec) / win return cur != prev } func moduleSchedulerOffset(moduleID string, win int64) int64 { if win <= 0 { return 0 } return int64(fnv32a(moduleID) % uint32(win)) } func fnv32a(s string) uint32 { h := fnv.New32a() _, _ = h.Write([]byte(s)) return h.Sum32() }