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
|
mu sync.Mutex
|
||||||
last string // last rendered output (de-dup)
|
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 —
|
// Render writes the config from a NodeConfig + anycast set. Idempotent —
|
||||||
// if the rendered content matches what we last wrote, no birdc reload.
|
// 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 {
|
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
|
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() {
|
func (b *BirdManager) scheduleReload() {
|
||||||
if b.debounce != nil {
|
now := time.Now()
|
||||||
b.debounce.Stop()
|
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() {
|
func (b *BirdManager) reload() {
|
||||||
|
|||||||
Reference in New Issue
Block a user