fix: sentinel value collision in FailoverProvider cooldown (#125) (#154)

ProviderCooldown used 0 as both the "not in cooldown" sentinel and a
valid timestamp from now_nanos(), so activate_cooldown(0) would silently
fail to activate. Store max(now_nanos, 1) to keep 0 reserved.

Closes #125

Co-authored-by: Claude Opus 4.6 <[email protected]>
This commit is contained in:
alexthebuildr
2026-02-18 08:17:27 +00:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 96d5fc0d39
commit 3669a7b1cd
+11 -1
View File
@@ -105,8 +105,9 @@ impl ProviderCooldown {
/// Activate cooldown at the given timestamp.
fn activate_cooldown(&self, now_nanos: u64) {
// Ensure 0 remains a safe "not in cooldown" sentinel.
self.cooldown_activated_nanos
.store(now_nanos, Ordering::Relaxed);
.store(now_nanos.max(1), Ordering::Relaxed);
}
/// Reset failure count and clear cooldown (called on success).
@@ -1041,6 +1042,15 @@ mod tests {
assert!(result.is_err());
}
// Test: activate_cooldown(0) still activates cooldown (sentinel collision fix).
#[test]
fn cooldown_at_nanos_zero_still_activates() {
let cd = ProviderCooldown::new();
cd.activate_cooldown(0);
assert!(cd.is_in_cooldown(0, 1000));
assert_eq!(cd.cooldown_activated_nanos.load(Ordering::Relaxed), 1);
}
// Test: set_model propagates to all providers and active_model_name reflects change.
#[test]
fn set_model_propagates_to_all_providers() {