fix(pool): allow zero cooldown settings

This commit is contained in:
elky
2026-06-21 12:20:08 +08:00
parent 61bdd304b7
commit 900f8a7163
2 changed files with 90 additions and 7 deletions
@@ -458,12 +458,10 @@ pub(crate) fn admin_provider_pool_config_from_config_value(
rate_limit_cooldown_seconds: pool_advanced
.get("rate_limit_cooldown_seconds")
.and_then(json_u64)
.filter(|value| *value > 0)
.unwrap_or(300),
overload_cooldown_seconds: pool_advanced
.get("overload_cooldown_seconds")
.and_then(json_u64)
.filter(|value| *value > 0)
.unwrap_or(30),
probing_enabled: pool_advanced
.get("probing_enabled")
@@ -672,6 +670,20 @@ mod tests {
assert_eq!(config.sticky_session_ttl_seconds, 0);
}
#[test]
fn parses_zero_cooldown_seconds_to_disable_error_cooldowns() {
let config = admin_provider_pool_config_from_config_value(Some(&json!({
"pool_advanced": {
"rate_limit_cooldown_seconds": 0,
"overload_cooldown_seconds": 0
}
})))
.expect("pool config should parse");
assert_eq!(config.rate_limit_cooldown_seconds, 0);
assert_eq!(config.overload_cooldown_seconds, 0);
}
#[test]
fn parses_legacy_pool_score_weights_from_pool_advanced() {
let config = admin_provider_pool_config_from_config_value(Some(&json!({
@@ -267,15 +267,20 @@ fn resolve_transient_cooldown_ttl(
retry_after_seconds: Option<u64>,
pool_config: &AdminProviderPoolConfig,
) -> u64 {
let configured_cooldown_seconds = if status_code == 429 {
pool_config.rate_limit_cooldown_seconds
} else {
pool_config.overload_cooldown_seconds
};
if configured_cooldown_seconds == 0 {
return 0;
}
if matches!(status_code, 429 | 503) {
if let Some(retry_after_seconds) = retry_after_seconds {
return retry_after_seconds;
}
}
if status_code == 429 {
return pool_config.rate_limit_cooldown_seconds;
}
pool_config.overload_cooldown_seconds
configured_cooldown_seconds
}
async fn set_pool_cooldown(
@@ -586,7 +591,7 @@ mod tests {
use super::{
admin_provider_pool_key_terminal_error_reason, parse_google_quota_cooldown_seconds_at,
record_admin_provider_pool_error, record_admin_provider_pool_stream_timeout,
record_admin_provider_pool_success,
record_admin_provider_pool_success, resolve_transient_cooldown_ttl,
};
use crate::handlers::admin::provider::pool::runtime::reads::read_admin_provider_pool_runtime_state;
use crate::handlers::admin::provider::shared::support::{
@@ -781,6 +786,23 @@ mod tests {
);
}
#[test]
fn zero_cooldown_settings_disable_retry_after_transient_cooldowns() {
let mut pool_config = sample_pool_config();
pool_config.rate_limit_cooldown_seconds = 0;
pool_config.overload_cooldown_seconds = 0;
assert_eq!(
resolve_transient_cooldown_ttl(429, Some(120), &pool_config),
0
);
assert_eq!(
resolve_transient_cooldown_ttl(503, Some(120), &pool_config),
0
);
assert_eq!(resolve_transient_cooldown_ttl(500, None, &pool_config), 0);
}
#[tokio::test]
async fn success_feedback_writes_sticky_lru_cost_and_latency() {
let Some(redis) = start_managed_redis_or_skip().await else {
@@ -948,6 +970,55 @@ mod tests {
.is_some_and(|ttl| *ttl <= 120 && *ttl >= 100));
}
#[tokio::test]
async fn error_feedback_does_not_write_cooldown_when_429_or_529_cooldown_is_zero() {
let Some(redis) = start_managed_redis_or_skip().await else {
return;
};
let app = build_runner_app(redis.redis_url(), "pool_runtime_zero_cooldown").await;
let runtime = app.runtime_state.as_ref();
let mut pool_config = sample_pool_config();
pool_config.rate_limit_cooldown_seconds = 0;
pool_config.overload_cooldown_seconds = 0;
let key_ids = vec!["key-429".to_string(), "key-529".to_string()];
record_admin_provider_pool_error(
runtime,
"provider-1",
"key-429",
&pool_config,
429,
Some(r#"{"error":{"message":"rate limited"}}"#),
Some(&BTreeMap::from([(
"Retry-After".to_string(),
"120".to_string(),
)])),
)
.await;
record_admin_provider_pool_error(
runtime,
"provider-1",
"key-529",
&pool_config,
529,
Some(r#"{"error":{"message":"overloaded"}}"#),
None,
)
.await;
let runtime = read_admin_provider_pool_runtime_state(
runtime,
"provider-1",
&key_ids,
&pool_config,
None,
)
.await;
assert!(runtime.cooldown_reason_by_key.is_empty());
assert!(runtime.cooldown_ttl_by_key.is_empty());
}
#[tokio::test]
async fn error_feedback_removes_active_probe_member_when_key_becomes_unschedulable() {
let Some(redis) = start_managed_redis_or_skip().await else {