Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
43 lines
1.2 KiB
Go
43 lines
1.2 KiB
Go
package pipeline
|
|
|
|
import (
|
|
"hash/fnv"
|
|
"time"
|
|
|
|
"evobgp/internal/store"
|
|
)
|
|
|
|
// SchedulerTickSec matches the scheduler ticker interval (internal/scheduler).
|
|
// The module refresh window stays at least 60s; a shorter tick only notices the boundary sooner.
|
|
const SchedulerTickSec = 10
|
|
|
|
// 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()
|
|
}
|