Fix provider key response time overflow

This commit is contained in:
yangrs
2026-07-09 14:40:48 +08:00
parent 5e1d14f19b
commit 192dc6c20d
9 changed files with 43 additions and 16 deletions
@@ -1238,7 +1238,7 @@ fn build_pool_catalog_key_context(
.filter(|count| *count > 0)
.zip(key.total_response_time_ms)
.map(|(success_count, total_response_time_ms)| {
f64::from(total_response_time_ms) / f64::from(success_count)
total_response_time_ms as f64 / f64::from(success_count)
})
.filter(|value| value.is_finite() && *value >= 0.0);
@@ -1202,7 +1202,7 @@ fn provider_query_pool_catalog_key_context(
.filter(|count| *count > 0)
.zip(key.total_response_time_ms)
.map(|(success_count, total_response_time_ms)| {
f64::from(total_response_time_ms) / f64::from(success_count)
total_response_time_ms as f64 / f64::from(success_count)
})
.filter(|value| value.is_finite() && *value >= 0.0);
@@ -112,7 +112,7 @@ pub(crate) async fn build_admin_keys_grouped_by_format_payload(
None
};
let avg_response_time_ms = if success_count > 0 {
Some(f64::from(key.total_response_time_ms.unwrap_or(0)) / success_count as f64)
Some(key.total_response_time_ms.unwrap_or(0) as f64 / success_count as f64)
} else {
None
};
@@ -2309,7 +2309,7 @@ pub(crate) fn build_admin_provider_key_response(
let request_count = u64::from(key.request_count.unwrap_or(0));
let success_count = u64::from(key.success_count.unwrap_or(0));
let error_count = u64::from(key.error_count.unwrap_or(0));
let total_response_time_ms = f64::from(key.total_response_time_ms.unwrap_or(0));
let total_response_time_ms = key.total_response_time_ms.unwrap_or(0) as f64;
let success_rate = if request_count > 0 {
success_count as f64 / request_count as f64
} else {
@@ -278,7 +278,7 @@ pub struct StoredProviderCatalogKey {
pub total_cost_usd: f64,
pub success_count: Option<u32>,
pub error_count: Option<u32>,
pub total_response_time_ms: Option<u32>,
pub total_response_time_ms: Option<u64>,
pub last_used_at_unix_secs: Option<u64>,
pub auto_fetch_models: bool,
pub last_models_fetch_at_unix_secs: Option<u64>,
@@ -444,7 +444,7 @@ impl StoredProviderCatalogKey {
pub fn with_usage_fields(
mut self,
error_count: Option<u32>,
total_response_time_ms: Option<u32>,
total_response_time_ms: Option<u64>,
) -> Self {
self.error_count = error_count;
self.total_response_time_ms = total_response_time_ms;
@@ -75,7 +75,7 @@ impl InMemoryProviderCatalogReadRepository {
));
key.total_tokens = apply_i64_delta_to_u64(key.total_tokens, delta.total_tokens);
key.total_cost_usd = apply_f64_delta(key.total_cost_usd, delta.total_cost_usd);
key.total_response_time_ms = Some(apply_i64_delta_to_u32(
key.total_response_time_ms = Some(apply_i64_delta_to_u64(
key.total_response_time_ms.unwrap_or_default(),
delta.total_response_time_ms,
));
@@ -123,7 +123,7 @@ impl InMemoryProviderCatalogReadRepository {
key.total_tokens = clamp_i64_to_u64(contribution.total_tokens);
key.total_cost_usd = contribution.total_cost_usd.max(0.0);
key.total_response_time_ms =
Some(clamp_i64_to_u32(contribution.total_response_time_ms));
Some(clamp_i64_to_u64(contribution.total_response_time_ms));
key.last_used_at_unix_secs = contribution.last_used_at_unix_secs;
}
}
@@ -602,7 +602,13 @@ WHERE id = ?
.bind(key.total_cost_usd)
.bind(optional_i64_from_u32(key.success_count).unwrap_or(0))
.bind(optional_i64_from_u32(key.error_count).unwrap_or(0))
.bind(optional_i64_from_u32(key.total_response_time_ms).unwrap_or(0))
.bind(
optional_i64_from_u64(
key.total_response_time_ms,
"provider_api_keys.total_response_time_ms",
)?
.unwrap_or(0),
)
.bind(optional_i64_from_u64(
key.last_used_at_unix_secs,
"provider_api_keys.last_used_at",
@@ -1542,7 +1548,7 @@ fn map_key_row(row: &MySqlRow) -> Result<StoredProviderCatalogKey, DataLayerErro
row.try_get("error_count").map_sql_err()?,
"provider_api_keys.error_count",
)?,
optional_u32(
optional_u64(
row.try_get("total_response_time_ms").map_sql_err()?,
"provider_api_keys.total_response_time_ms",
)?,
@@ -1301,7 +1301,17 @@ INSERT INTO provider_api_keys (
.bind(key.total_cost_usd)
.bind(key.success_count.map(i64::from))
.bind(key.error_count.map(i64::from))
.bind(key.total_response_time_ms.map(i64::from))
.bind(
key.total_response_time_ms
.map(|value| {
i64::try_from(value).map_err(|_| {
DataLayerError::InvalidInput(format!(
"provider catalog key.total_response_time_ms exceeds i64: {value}"
))
})
})
.transpose()?,
)
.bind(key.last_used_at_unix_secs.map(|value| value as f64))
.bind(key.last_models_fetch_at_unix_secs.map(|value| value as f64))
.bind(&key.last_models_fetch_error)
@@ -2378,7 +2388,7 @@ fn map_key_row(row: &PgRow) -> Result<StoredProviderCatalogKey, DataLayerError>
.transpose()?;
let total_response_time_ms = row_get::<Option<i64>>(row, "total_response_time_ms")?
.map(|value| {
u32::try_from(value).map_err(|_| {
u64::try_from(value).map_err(|_| {
DataLayerError::UnexpectedValue(format!(
"invalid provider_api_keys.total_response_time_ms: {value}"
))
@@ -1026,7 +1026,13 @@ WHERE id = ?
.bind(key.total_cost_usd)
.bind(optional_i64_from_u32(key.success_count).unwrap_or(0))
.bind(optional_i64_from_u32(key.error_count).unwrap_or(0))
.bind(optional_i64_from_u32(key.total_response_time_ms).unwrap_or(0))
.bind(
optional_i64_from_u64(
key.total_response_time_ms,
"provider_api_keys.total_response_time_ms",
)?
.unwrap_or(0),
)
.bind(optional_i64_from_u64(
key.last_used_at_unix_secs,
"provider_api_keys.last_used_at",
@@ -2008,7 +2014,7 @@ fn map_key_row(row: &SqliteRow) -> Result<StoredProviderCatalogKey, DataLayerErr
row.try_get("error_count").map_sql_err()?,
"provider_api_keys.error_count",
)?,
optional_u32(
optional_u64(
row.try_get("total_response_time_ms").map_sql_err()?,
"provider_api_keys.total_response_time_ms",
)?,
@@ -2151,6 +2157,7 @@ mod tests {
.expect("keys should list");
assert_eq!(keys.len(), 1);
assert_eq!(keys[0].total_tokens, 1234);
assert_eq!(keys[0].total_response_time_ms, Some(u32::MAX as u64 + 1));
assert_eq!(keys[0].concurrent_limit, Some(3));
let page = repository
@@ -2303,7 +2310,7 @@ mod tests {
Some(10),
Some(9),
)
.with_usage_fields(Some(1), Some(250))
.with_usage_fields(Some(1), Some(u32::MAX as u64 + 42))
.with_usage_totals(1234, 1.5)
.with_health_fields(
Some(json!({"openai:chat":{"score":1}})),
@@ -2317,6 +2324,10 @@ mod tests {
.expect("key should create");
assert_eq!(created_key.concurrent_limit, Some(3));
assert_eq!(created_key.total_tokens, 1234);
assert_eq!(
created_key.total_response_time_ms,
Some(u32::MAX as u64 + 42)
);
assert_eq!(
created_key.last_models_fetch_error.as_deref(),
Some("stale models fetch error")
@@ -2443,7 +2454,7 @@ INSERT INTO provider_api_keys (
) VALUES (
'key-1', 'provider-1', 'default', 'enc-key', 'api_key',
'{"cache_1h":true}', 1, '["openai:chat"]', '{"openai:chat":"api_key"}',
5, 120, 3, 10, 1234, 1.5, 9, 1, 250, '{"openai:chat":{"score":1}}',
5, 120, 3, 10, 1234, 1.5, 9, 1, 4294967296, '{"openai:chat":{"score":1}}',
5, 6
)
"#,