缓存问题修复

This commit is contained in:
ZheFox
2026-07-16 18:55:28 +08:00
parent 715a5ed626
commit f009fb73c3
5 changed files with 148 additions and 6 deletions
@@ -85,6 +85,7 @@ fn merge_codex_reset_credit_detail_metadata(
fn mark_codex_reset_credit_detail_failed(
codex_metadata: &mut Map<String, Value>,
updated_at_unix_secs: u64,
detail_error: impl Into<String>,
) {
let mut reset_credits = codex_metadata
@@ -92,6 +93,7 @@ fn mark_codex_reset_credit_detail_failed(
.and_then(Value::as_object)
.cloned()
.unwrap_or_default();
reset_credits.insert("updated_at".to_string(), json!(updated_at_unix_secs));
reset_credits.insert("detail_source".to_string(), json!("wham_readonly"));
reset_credits.insert("detail_status".to_string(), json!("failed"));
reset_credits.insert(
@@ -116,7 +118,7 @@ async fn enrich_codex_reset_credit_details(
{
Ok(request_spec) => request_spec,
Err(message) => {
mark_codex_reset_credit_detail_failed(codex_metadata, message);
mark_codex_reset_credit_detail_failed(codex_metadata, now_unix_secs, message);
return Ok(());
}
};
@@ -129,6 +131,7 @@ async fn enrich_codex_reset_credit_details(
ProviderQuotaExecutionOutcome::Failure(detail) => {
mark_codex_reset_credit_detail_failed(
codex_metadata,
now_unix_secs,
format!("reset credit detail 请求执行失败: {detail}"),
);
return Ok(());
@@ -140,6 +143,7 @@ async fn enrich_codex_reset_credit_details(
.unwrap_or_else(|| format!("HTTP {}", result.status_code));
mark_codex_reset_credit_detail_failed(
codex_metadata,
now_unix_secs,
format!(
"reset credit detail 返回状态码 {}: {detail}",
result.status_code
@@ -153,7 +157,11 @@ async fn enrich_codex_reset_credit_details(
.as_ref()
.and_then(|body| body.json_body.as_ref())
else {
mark_codex_reset_credit_detail_failed(codex_metadata, "无法解析 reset credit detail 响应");
mark_codex_reset_credit_detail_failed(
codex_metadata,
now_unix_secs,
"无法解析 reset credit detail 响应",
);
return Ok(());
};
if let Some(detail_metadata) =
@@ -161,7 +169,11 @@ async fn enrich_codex_reset_credit_details(
{
merge_codex_reset_credit_detail_metadata(codex_metadata, &detail_metadata);
} else {
mark_codex_reset_credit_detail_failed(codex_metadata, "reset credit detail 响应为空");
mark_codex_reset_credit_detail_failed(
codex_metadata,
now_unix_secs,
"reset credit detail 响应为空",
);
}
Ok(())
@@ -792,4 +804,19 @@ mod tests {
Some(&json!(2u64))
);
}
#[test]
fn codex_reset_credit_detail_failure_records_attempt_time() {
let mut metadata = Map::new();
mark_codex_reset_credit_detail_failed(&mut metadata, 1_777_000_000, "request failed");
assert_eq!(
metadata
.get("reset_credits")
.and_then(Value::as_object)
.and_then(|credits| credits.get("updated_at")),
Some(&json!(1_777_000_000u64))
);
}
}
@@ -2054,6 +2054,29 @@ fn quota_snapshot_has_materialized_data(
})
}
fn codex_upstream_metadata_is_at_least_as_fresh(
quota_snapshot: Option<&Map<String, Value>>,
upstream_metadata: Option<&Value>,
) -> bool {
let Some(metadata) = provider_quota_metadata_bucket(upstream_metadata, "codex") else {
return false;
};
let Some(metadata_updated_at) = metadata
.get("updated_at")
.and_then(admin_provider_quota_pure::coerce_json_u64)
else {
return false;
};
let snapshot_updated_at = quota_snapshot.and_then(|quota| {
quota
.get("updated_at")
.or_else(|| quota.get("observed_at"))
.and_then(admin_provider_quota_pure::coerce_json_u64)
});
snapshot_updated_at.is_none_or(|updated_at| metadata_updated_at >= updated_at)
}
fn windsurf_quota_snapshot_has_stale_cooldown(quota_snapshot: &Map<String, Value>) -> bool {
let code = quota_snapshot
.get("code")
@@ -2121,8 +2144,15 @@ pub(crate) fn provider_key_status_snapshot_payload(
.and_then(Value::as_object)
.and_then(|snapshot| snapshot.get("quota"))
.and_then(Value::as_object);
let refresh_codex_snapshot = provider_type.trim().eq_ignore_ascii_case("codex")
&& codex_upstream_metadata_is_at_least_as_fresh(
quota_snapshot,
key.upstream_metadata.as_ref(),
);
let payload = if quota_snapshot_has_materialized_data(quota_snapshot, provider_type) {
let payload = if quota_snapshot_has_materialized_data(quota_snapshot, provider_type)
&& !refresh_codex_snapshot
{
status_snapshot
.cloned()
.unwrap_or_else(default_provider_key_status_snapshot)
@@ -3512,6 +3542,58 @@ mod tests {
);
}
#[test]
fn provider_key_status_snapshot_payload_restores_complete_codex_cache() {
let mut key = sample_catalog_key();
key.upstream_metadata = Some(json!({
"codex": {
"updated_at": 200u64,
"plan_type": "plus",
"primary_used_percent": 89.0,
"primary_reset_at": 1_900_000_000u64,
"spark_primary_used_percent": 40.0,
"spark_primary_reset_at": 1_900_100_000u64,
"reset_credits": {
"available_count": 3,
"updated_at": 200u64,
"detail_status": "available",
"credits": []
}
}
}));
key.status_snapshot = Some(json!({
"quota": {
"version": 2,
"provider_type": "codex",
"updated_at": 200u64,
"windows": [{
"code": "weekly",
"used_ratio": 1.0,
"reset_at": 1_900_000_000u64
}]
}
}));
let payload = provider_key_status_snapshot_payload(&key, "codex");
let windows = payload["quota"]["windows"]
.as_array()
.expect("quota windows should exist");
assert_eq!(payload.pointer("/quota/plan_type"), Some(&json!("plus")));
assert_eq!(
payload.pointer("/quota/reset_credits/available_count"),
Some(&json!(3u64))
);
assert!(windows.iter().any(|window| window["code"] == "spark_5h"));
assert_eq!(
windows
.iter()
.find(|window| window["code"] == "weekly")
.and_then(|window| window.get("used_ratio")),
Some(&json!(0.89))
);
}
#[test]
fn sync_provider_key_quota_status_snapshot_preserves_codex_usage_state() {
let current_status_snapshot = json!({