| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
refactor(quota): 限流配额归一化 — limiter 成为唯一存储,删 QuotaStore 老板报告的 bug:key plan 配 rpm_limit=2 实际只允许 1 个请求。根因是 effective_limits() 把 rpm 单独透传,调用方又同时把 rpm 字段和含 60s counts 的 windows 都传给 limiter,commit_record 对同一 cache_key 写两次。 归一化重构从三层防线堵死双重统计路径: 1. 数据流:effective_limits() 返回 (concurrency, windows, stale) 三元组, 不再单独透传 rpm。rpm_limit/tpm_limit 由 merge_shorthand 折叠进 windows 的 60s 项的 counts/tokens 字段。PlanCharge 同步删 rpm_limit 字段, 24 字段收敛到 17。 2. 实现:commit_counts / settle_usage 内部按 window_secs 去重 — 即使 windows 里有重复 secs,每个 cache_key 也只 bump 一次。 SlidingWindowLimiter.WindowCounter 扩展为三维度(counts/tokens/costs_micros), 一个 cache_key 同时存三维度,与 WindowLimit 结构对齐。 3. 回归测试:test_rpm_limit_2_actually_allows_2_requests_e2e 端到端 复现老板原 bug 场景,验证 rpm_limit=2 真的允许 2 个请求。 test_tpm_limit_tokens_window_no_double_settle 对 tpm 做同样验证。 test_rpm_field_form_equals_window_limits_form 锁定两种 YAML 形式 产生相同 effective_limits 输出。 副作用: - boom-quota crate 整体删除。cumulative 表 DDL 迁移到 boom-limiter::migrations, cache_key 格式(kc:{kh}:{kind} / tc:{tid}:{kind})和 boom_rate_limit_cumulative 表名不变,无需 DB 数据迁移。 - spawn_quota_sync_task 合并到 spawn_sync_task(10 分钟周期)。 - dashboard 30+ callsite 改为 limiter.* / boom_limiter::*。 - dashboard 旧的 effective_limits() 4 元组解构改为 3 元组, rpm_limit 从 60s window_limits.counts 字段提取以保持前端兼容。 - 新增 state_alter_ddl() 给现有 boom_rate_limit_state 加 tokens + costs_micros 列(幂等 ALTER)。 - 删除 boom-core 里失效的 RateLimiter trait。 测试:cargo test --workspace 126 passing,唯一失败是 pre-existing 的 hybrid_router::tests::code_request_routes_to_large(已确认 master 上同样失败)。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: liqiang <liqiang64@huawei.com> | 14 天前 | |
feat: 独立 boom-quota 模块 + team/key 双层配额计费体系 新增 boom-quota crate 作为配额计费的独立模块(仅依赖 boom-core), 集中后续配额相关演进。实现完整的 team+key 双层限制: - team: concurrency / rpm / window / tpm-1min / total-tpm / total-cost - key: concurrency / rpm / window / tpm-1min / total-tpm / total-cost - key 限制被框在 team 之下;任一层超限即拒绝,错误信息含 scope/ scope_id/plan_name/limit_type。 - 计费 token 数来自 vllm 返回(非自算),cost 按 model_info 中 input/output_cost_per_million_tokens 费率计算。 缓存命中/不命中分计费: - ModelCostRate 新增 cached_input_cost_per_token 字段;compute_cost 按 (input-cached)×input价 + cached×cached价 + output×output价 计算。 - cached_tokens 数据来自 vllm usage chunk 的 prompt_tokens_details.cached_tokens,经 UsageTrackerState 传递到 LoggedStream::drop / 非流式响应完成时调 PlanCharge::settle。 - cached_input_cost_per_million_tokens 缺省 0 时回退为按 input 价计 (向后兼容)。 计费模板(cost_templates): - 顶层配置 cost_templates 定义可复用的命名费率模板(input/cached/ output 三档价)。 - model_info.cost_template 引用模板名;模板字段优先于 inline 字段, 避免歧义。模板名找不到时 warn 并回退到 inline 字段,不静默按 0 计费。 配置面单位改为百万 token: - YAML 字段重命名为 *_per_million_tokens(input/cached/output), 值用 0.27 而非 0.00000027,配置更直观。 - state.rs 解析时把 per_million_tokens 除以 1e6 转为内部 per-token Decimal 费率。ModelCostRate 内部字段名保持 *_per_token(计费粒度), compute_cost / settle_usage 公式不变;DB cost 仍存 micros(计算后金额, 非费率),向后兼容已有数据。 持久化:cumulative counters 持久化到 boom_rate_limit_cumulative, 1-min TPM 窗口复用 boom_rate_limit_state。30s dirty-sync 后台任务 保证崩溃损失 ≤ 30s;启动时 restore_from_db 全量恢复。 架构: - boom-quota 仅依赖 boom-core(叶子模块)。 - 不破坏既有 AppState 生命周期 / AdminCommand 模式 / 热加载规则。 - PlanStore 扩展 team_assignments,team plan 拒绝分配给 key(warn + default_plan 回退),member_plan 为 team 下 key 的默认 plan。 - PlanType 在 boom-core 定义为单一来源,boom-config / boom-limiter 均从 boom_core::types::PlanType re-export。 - cost 在 DB 存为 BIGINT micros (1e-6 USD),避免 rust_decimal 的 sqlx 集成问题。 Dashboard 配额看板(本次新增): - DashboardState 加 quota_store: Arc<QuotaStore> 字段,main.rs 调用 方同步更新。 - /user/usage 重写为多维 window + cumulative:按 window_secs 分组, 每组 dims 字典含 counts/tokens/costs,cumulative 单独一块。 - 6 个 admin quota 端点:/admin/quota/{overview,team/{id},unassigned, key/{hash}/windows,reset/key/{hash},reset/team/{id}};累计数据 全部用 SQL JOIN 拿,避免 N+1 内存扫描。 - QuotaStore 加 peek_key_windows / peek_team_windows / scan_windows_with_prefix 批量读 API;WindowInfo 结构暴露给 dashboard。 - 前端 normalizeWindowLimit 兼容 2 元素数组、4 元素数组、对象三种 WindowLimit 序列化格式;renderPlan/renderUsage/renderPlansTable 全部修复;多维 window 合并成一张宽卡,cumulative 单独一张。 - 新增 admin-quota section:overview → team 详情/无 team 详情切换, 分页+搜索+排序+reset key/team(key 数量上万时分页 + 搜索足够)。 Signed-off-by: liqiang <liqiang64@huawei.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: liqiang <liqiang64@huawei.com> | 17 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 14 天前 | ||
| 17 天前 |