mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 09:50:21 +08:00
perf(usage): heatmap 改为数据库端按天聚合查询
将 admin 和 user heatmap 从逐条加载 usage audit 记录后在应用层聚合, 改为通过 SQL GROUP BY DATE 在数据库端直接按天汇总, 大幅减少数据传输量。 新增 UsageDailyHeatmapQuery / StoredUsageDailySummary 类型, 在 trait、SQL、内存实现中均补齐 summarize_usage_daily_heatmap 方法。 同时优化 docker-compose: postgres 增加空闲事务超时与 keepalive 参数, gateway 增加健康检查配置。
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
use crate::handlers::admin::request::AdminAppState;
|
||||
use crate::GatewayError;
|
||||
use aether_admin::observability::stats::round_to;
|
||||
use aether_admin::observability::usage::{
|
||||
admin_usage_data_unavailable_response, admin_usage_heatmap_json,
|
||||
ADMIN_USAGE_DATA_UNAVAILABLE_DETAIL,
|
||||
admin_usage_data_unavailable_response, ADMIN_USAGE_DATA_UNAVAILABLE_DETAIL,
|
||||
};
|
||||
use aether_data_contracts::repository::usage::UsageAuditListQuery;
|
||||
use aether_data_contracts::repository::usage::UsageDailyHeatmapQuery;
|
||||
use axum::{
|
||||
body::Body,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub(super) async fn build_admin_usage_heatmap_response(
|
||||
state: &AdminAppState<'_>,
|
||||
@@ -19,14 +21,64 @@ pub(super) async fn build_admin_usage_heatmap_response(
|
||||
ADMIN_USAGE_DATA_UNAVAILABLE_DETAIL,
|
||||
));
|
||||
}
|
||||
let now_unix_secs = u64::try_from(chrono::Utc::now().timestamp()).unwrap_or_default();
|
||||
let created_from_unix_secs = now_unix_secs.saturating_sub(365 * 24 * 3600);
|
||||
let mut usage = state
|
||||
.list_usage_audits(&UsageAuditListQuery {
|
||||
created_from_unix_secs: Some(created_from_unix_secs),
|
||||
..Default::default()
|
||||
let today = chrono::Utc::now().date_naive();
|
||||
let start_date = today
|
||||
.checked_sub_signed(chrono::Duration::days(364))
|
||||
.unwrap_or(today);
|
||||
let created_from_unix_secs = u64::try_from(
|
||||
chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
|
||||
start_date.and_hms_opt(0, 0, 0).unwrap_or_default(),
|
||||
chrono::Utc,
|
||||
)
|
||||
.timestamp(),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
let summaries = state
|
||||
.summarize_usage_daily_heatmap(&UsageDailyHeatmapQuery {
|
||||
created_from_unix_secs,
|
||||
user_id: None,
|
||||
admin_mode: true,
|
||||
})
|
||||
.await?;
|
||||
usage.retain(|item| item.status != "pending" && item.status != "streaming");
|
||||
Ok(Json(admin_usage_heatmap_json(&usage)).into_response())
|
||||
|
||||
let grouped: BTreeMap<String, _> = summaries.into_iter().map(|s| (s.date.clone(), s)).collect();
|
||||
|
||||
let mut max_requests = 0_u64;
|
||||
let mut cursor = start_date;
|
||||
let mut days = Vec::new();
|
||||
while cursor <= today {
|
||||
let date_str = cursor.to_string();
|
||||
let (requests, total_tokens, total_cost, actual_total_cost) =
|
||||
if let Some(s) = grouped.get(&date_str) {
|
||||
(
|
||||
s.requests,
|
||||
s.total_tokens,
|
||||
s.total_cost_usd,
|
||||
s.actual_total_cost_usd,
|
||||
)
|
||||
} else {
|
||||
(0, 0, 0.0, 0.0)
|
||||
};
|
||||
max_requests = max_requests.max(requests);
|
||||
days.push(json!({
|
||||
"date": date_str,
|
||||
"requests": requests,
|
||||
"total_tokens": total_tokens,
|
||||
"total_cost": round_to(total_cost, 6),
|
||||
"actual_total_cost": round_to(actual_total_cost, 6),
|
||||
}));
|
||||
cursor = cursor
|
||||
.checked_add_signed(chrono::Duration::days(1))
|
||||
.unwrap_or(today + chrono::Duration::days(1));
|
||||
}
|
||||
|
||||
Ok(Json(json!({
|
||||
"start_date": start_date.to_string(),
|
||||
"end_date": today.to_string(),
|
||||
"total_days": days.len(),
|
||||
"max_requests": max_requests,
|
||||
"days": days,
|
||||
}))
|
||||
.into_response())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user