fix(adaptive): 修复 429 计数归零写入及 reset 语义

- sql.rs: UPDATE 时对 concurrent_429_count / rpm_429_count 加 COALESCE(, 0),避免 None 覆写为 NULL
- effects.rs: rpm_429_count 始终写入 Some(projection.rpm_429_count),不再过滤零值;新增 unknown-429 场景下零值持久化的单元测试
- adaptive.rs: reset 接口将两个计数字段置为 Some(0) 而非 None,与数据库默认语义对齐;同步修正集成测试断言
- docker-compose.build.yml: 补充 AETHER_GATEWAY_AUTO_PREPARE_DATABASE 默认开启
- README.md: 同步说明 build compose 也已默认开启自动迁移
This commit is contained in:
fawney19
2026-04-22 22:16:51 +08:00
parent c8d7dbd8d6
commit 4374f53315
6 changed files with 45 additions and 10 deletions

View File

@@ -134,8 +134,8 @@ impl<'a> AdminAppState<'a> {
return Ok(admin_adaptive_key_not_found_response(key_id));
};
key.learned_rpm_limit = None;
key.concurrent_429_count = None;
key.rpm_429_count = None;
key.concurrent_429_count = Some(0);
key.rpm_429_count = Some(0);
key.last_429_at_unix_secs = None;
key.last_429_type = None;
key.adjustment_history = None;

View File

@@ -300,7 +300,7 @@ async fn record_adaptive_rate_limit_effect(
};
let mut updated_key = current_key.clone();
updated_key.rpm_429_count = Some(projection.rpm_429_count).filter(|value| *value > 0);
updated_key.rpm_429_count = Some(projection.rpm_429_count);
updated_key.learned_rpm_limit = projection.learned_rpm_limit;
updated_key.last_429_at_unix_secs = Some(projection.last_429_at_unix_secs);
updated_key.last_429_type = Some(projection.last_429_type);
@@ -1315,6 +1315,40 @@ mod tests {
assert_eq!(stored_key.last_429_type, None);
}
#[tokio::test]
async fn adaptive_rate_limit_effect_persists_zero_rpm_count_for_unknown_429() {
let mut key = sample_health_key();
key.rpm_limit = None;
key.learned_rpm_limit = Some(20);
let state = adaptive_state_with_request_candidates(key, Vec::new());
let plan = sample_plan();
apply_local_execution_effect(
&state,
LocalExecutionEffectContext {
plan: &plan,
report_context: None,
},
LocalExecutionEffect::AdaptiveRateLimit(LocalAdaptiveRateLimitEffect {
status_code: 429,
classification: LocalFailoverClassification::RetryStatusCode,
headers: None,
}),
)
.await;
let stored_key = state
.read_provider_catalog_keys_by_ids(std::slice::from_ref(&plan.key_id))
.await
.expect("provider catalog keys should load")
.into_iter()
.next()
.expect("stored key should exist");
assert_eq!(stored_key.rpm_429_count, Some(0));
assert_eq!(stored_key.learned_rpm_limit, Some(19));
assert_eq!(stored_key.last_429_type.as_deref(), Some("unknown"));
}
#[tokio::test]
async fn adaptive_success_effect_expands_limit_from_recent_rpm_usage() {
let now_unix_secs = chrono::Utc::now().timestamp().max(0) as u64;

View File

@@ -440,8 +440,8 @@ async fn gateway_resets_admin_adaptive_learning_locally_with_trusted_admin_princ
.await
.expect("repository query should succeed");
assert_eq!(updated[0].learned_rpm_limit, None);
assert_eq!(updated[0].concurrent_429_count, None);
assert_eq!(updated[0].rpm_429_count, None);
assert_eq!(updated[0].concurrent_429_count, Some(0));
assert_eq!(updated[0].rpm_429_count, Some(0));
assert_eq!(updated[0].adjustment_history, None);
gateway_handle.abort();