fix(saas): harden model group failover + relay reliability
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

- cache: insert-then-retain pattern avoids empty-window race during refresh
- relay: manage_task_status flag for proper failover state transitions
- relay: retry_task re-resolves model groups instead of blind provider reuse
- relay: filter empty-member groups from available models list
- relay: quota cache stale entry cleanup (TTL 5x expiry)
- error: from_sqlx_unique helper for 409 vs 500 distinction
- model_config: unique constraint handling, duplicate member check
- model_config: failover_strategy whitelist, model_id vs group name conflict check
- model_config: group-scoped member removal with group_id validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-04-04 12:26:55 +08:00
parent 894c0d7b15
commit 5c48d62f7e
6 changed files with 221 additions and 64 deletions

View File

@@ -64,6 +64,18 @@ pub enum SaasError {
}
impl SaasError {
/// 将 sqlx::Error 中的 unique violation 映射为 AlreadyExists (409)
/// 其他 DB 错误保持为 Database (500)。
pub fn from_sqlx_unique(e: sqlx::Error, context: &str) -> Self {
if let sqlx::Error::Database(ref db_err) = e {
// PostgreSQL unique_violation = "23505"
if db_err.code().map(|c| c == "23505").unwrap_or(false) {
return Self::AlreadyExists(format!("{}已存在", context));
}
}
Self::Database(e)
}
/// 获取 HTTP 状态码
pub fn status_code(&self) -> StatusCode {
match self {