bird: leading-edge reload + 500ms cooldown (was trailing 500ms debounce)
Build flock Image / build (push) Has been cancelled
Build flock Image / build (push) Has been cancelled
A single Ready/NotReady transition no longer pays a 500ms reload wait — the first call to scheduleReload fires birdc immediately; further calls within 500ms are coalesced into one tail reload at the cooldown's end. Burst behavior is the same as before: under heavy churn (deploy rolling all replicas at once), at most one reload per 500ms. Steady-state latency from pod Ready transition to crt001 BGP withdraw: - probe period (set in pod spec, 1s minimum) - ~ms informer + reconcile + birdc + BGP UPDATE The 500ms hardcoded delay is gone. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+32
-5
@@ -26,9 +26,16 @@ type BirdManager struct {
|
||||
|
||||
mu sync.Mutex
|
||||
last string // last rendered output (de-dup)
|
||||
debounce *time.Timer
|
||||
cooldown *time.Timer
|
||||
cooldownEnd time.Time // window during which further reloads are coalesced
|
||||
pending bool // a render landed during cooldown; reload at window end
|
||||
}
|
||||
|
||||
// reloadCooldown is the minimum spacing between two birdc reloads. The
|
||||
// first change fires immediately (no leading-edge delay); follow-up
|
||||
// changes within this window are coalesced into a single tail reload.
|
||||
const reloadCooldown = 500 * time.Millisecond
|
||||
|
||||
// Render writes the config from a NodeConfig + anycast set. Idempotent —
|
||||
// if the rendered content matches what we last wrote, no birdc reload.
|
||||
func (b *BirdManager) Render(nc *flockv1alpha1.NodeConfig, anycast6, anycast4 []string, routerID string) error {
|
||||
@@ -88,12 +95,32 @@ func (b *BirdManager) Render(nc *flockv1alpha1.NodeConfig, anycast6, anycast4 []
|
||||
return nil
|
||||
}
|
||||
|
||||
// scheduleReload coalesces birdc reload calls into ~500ms windows.
|
||||
// scheduleReload uses leading-edge + cooldown semantics: the first call
|
||||
// reloads immediately; subsequent calls within reloadCooldown coalesce
|
||||
// into a single deferred reload at the cooldown's end. Caller holds b.mu.
|
||||
func (b *BirdManager) scheduleReload() {
|
||||
if b.debounce != nil {
|
||||
b.debounce.Stop()
|
||||
now := time.Now()
|
||||
if now.After(b.cooldownEnd) {
|
||||
// Outside any active cooldown — fire now (leading edge).
|
||||
b.cooldownEnd = now.Add(reloadCooldown)
|
||||
b.pending = false
|
||||
go b.reload()
|
||||
return
|
||||
}
|
||||
b.debounce = time.AfterFunc(500*time.Millisecond, b.reload)
|
||||
// Inside cooldown — coalesce. If no tail timer is set, schedule one
|
||||
// at the cooldown end; if already set, just leave it.
|
||||
if b.pending {
|
||||
return
|
||||
}
|
||||
b.pending = true
|
||||
delay := b.cooldownEnd.Sub(now)
|
||||
b.cooldown = time.AfterFunc(delay, func() {
|
||||
b.mu.Lock()
|
||||
b.pending = false
|
||||
b.cooldownEnd = time.Now().Add(reloadCooldown)
|
||||
b.mu.Unlock()
|
||||
b.reload()
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BirdManager) reload() {
|
||||
|
||||
Reference in New Issue
Block a user