mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 10:57:03 +08:00
fix(data): 同步模型调用次数至 global_models 读模型
- 模型列表改为读取 global_models.usage_count,避免每次扫描 usage 明细表 - 通过历史 backfill 与 usage upsert delta 维护该读模型 - 详情页保留实时 facts 兜底,统一排除 pending/streaming 状态
This commit is contained in:
@@ -405,7 +405,7 @@ SELECT
|
||||
gm.config,
|
||||
COALESCE(gm_stats.provider_count, 0) AS provider_count,
|
||||
COALESCE(gm_stats.active_provider_count, 0) AS active_provider_count,
|
||||
COALESCE(gm.usage_count, 0)::bigint AS usage_count,
|
||||
COALESCE(usage_stats.usage_count, gm.usage_count, 0)::bigint AS usage_count,
|
||||
EXTRACT(EPOCH FROM gm.created_at)::bigint AS created_at_unix_ms,
|
||||
EXTRACT(EPOCH FROM gm.updated_at)::bigint AS updated_at_unix_secs
|
||||
FROM global_models gm
|
||||
@@ -423,6 +423,12 @@ LEFT JOIN (
|
||||
JOIN providers p ON p.id = m.provider_id
|
||||
GROUP BY m.global_model_id
|
||||
) gm_stats ON gm_stats.global_model_id = gm.id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT NULLIF(COUNT(*), 0)::bigint AS usage_count
|
||||
FROM usage_billing_facts AS usage
|
||||
WHERE usage.model = gm.name
|
||||
AND usage.status NOT IN ('pending', 'streaming')
|
||||
) usage_stats ON TRUE
|
||||
WHERE gm.id = $1
|
||||
LIMIT 1
|
||||
"#,
|
||||
@@ -452,7 +458,7 @@ SELECT
|
||||
gm.config,
|
||||
COALESCE(gm_stats.provider_count, 0) AS provider_count,
|
||||
COALESCE(gm_stats.active_provider_count, 0) AS active_provider_count,
|
||||
COALESCE(gm.usage_count, 0)::bigint AS usage_count,
|
||||
COALESCE(usage_stats.usage_count, gm.usage_count, 0)::bigint AS usage_count,
|
||||
EXTRACT(EPOCH FROM gm.created_at)::bigint AS created_at_unix_ms,
|
||||
EXTRACT(EPOCH FROM gm.updated_at)::bigint AS updated_at_unix_secs
|
||||
FROM global_models gm
|
||||
@@ -470,6 +476,12 @@ LEFT JOIN (
|
||||
JOIN providers p ON p.id = m.provider_id
|
||||
GROUP BY m.global_model_id
|
||||
) gm_stats ON gm_stats.global_model_id = gm.id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT NULLIF(COUNT(*), 0)::bigint AS usage_count
|
||||
FROM usage_billing_facts AS usage
|
||||
WHERE usage.model = gm.name
|
||||
AND usage.status NOT IN ('pending', 'streaming')
|
||||
) usage_stats ON TRUE
|
||||
WHERE gm.name = $1
|
||||
LIMIT 1
|
||||
"#,
|
||||
@@ -1205,7 +1217,10 @@ fn map_provider_active_global_model_row(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{SqlxGlobalModelReadRepository, LIST_ADMIN_PROVIDER_MODELS_PREFIX};
|
||||
use super::{
|
||||
SqlxGlobalModelReadRepository, LIST_ADMIN_GLOBAL_MODELS_PREFIX,
|
||||
LIST_ADMIN_PROVIDER_MODELS_PREFIX,
|
||||
};
|
||||
use crate::postgres::{PostgresPoolConfig, PostgresPoolFactory};
|
||||
|
||||
const ADMIN_PROVIDER_MODEL_REQUIRED_COLUMNS: &[&str] = &[
|
||||
@@ -1241,6 +1256,64 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_global_model_sql_projects_usage_count_from_billing_facts() {
|
||||
let source = include_str!("sql.rs");
|
||||
|
||||
assert!(
|
||||
!LIST_ADMIN_GLOBAL_MODELS_PREFIX.contains("usage_billing_facts"),
|
||||
"admin global model list should read the maintained usage_count field"
|
||||
);
|
||||
assert!(
|
||||
source.contains("WHERE usage.model = gm.name"),
|
||||
"admin global model detail lookups should count usage facts for the selected model"
|
||||
);
|
||||
assert!(
|
||||
source.contains("AND usage.status NOT IN ('pending', 'streaming')"),
|
||||
"admin global model detail usage_count should use the maintained read-model status scope"
|
||||
);
|
||||
assert!(
|
||||
source.contains("COALESCE(usage_stats.usage_count, gm.usage_count, 0)::bigint AS usage_count"),
|
||||
"admin global model usage_count should prefer actual usage facts with stored-count fallback"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_global_model_list_sql_counts_usage_without_full_fact_aggregate() {
|
||||
assert!(
|
||||
LIST_ADMIN_GLOBAL_MODELS_PREFIX.contains("COALESCE(gm.usage_count, 0)::bigint AS usage_count"),
|
||||
"admin global model list should read maintained usage_count without per-request fact scans"
|
||||
);
|
||||
assert!(
|
||||
!LIST_ADMIN_GLOBAL_MODELS_PREFIX.contains("GROUP BY usage.model"),
|
||||
"admin global model list must not aggregate the full usage fact table before pagination"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_model_usage_count_read_model_has_backfill_and_delta_maintenance() {
|
||||
let backfill_sql =
|
||||
include_str!("../../../backfills/20260505120000_rebuild_global_model_usage_count.sql");
|
||||
let delta_sql = include_str!("../usage/sql/queries/apply_global_model_usage_delta_sql.sql");
|
||||
|
||||
assert!(
|
||||
backfill_sql.contains("UPDATE global_models AS gm"),
|
||||
"global model usage_count backfill should refresh the read model"
|
||||
);
|
||||
assert!(
|
||||
backfill_sql.contains("FROM usage_billing_facts AS usage"),
|
||||
"global model usage_count backfill should rebuild from canonical usage facts"
|
||||
);
|
||||
assert!(
|
||||
delta_sql.contains("UPDATE global_models"),
|
||||
"usage writes should maintain the global model usage_count read model"
|
||||
);
|
||||
assert!(
|
||||
delta_sql.contains("WHERE name = $1"),
|
||||
"global model usage_count delta should target models by canonical model name"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn repository_constructs_from_lazy_pool() {
|
||||
let factory = PostgresPoolFactory::new(PostgresPoolConfig {
|
||||
|
||||
Reference in New Issue
Block a user