feat(usage): 改进请求追踪与缓存预热

- 区分请求重试与故障转移:新增 has_retry 标识亲和缓存重试
- 修正候选 TTFB 计算:记录候选自身的首字节时间而非全局时间
- Streaming 状态同步 rate_multiplier,支持按 API 格式配置
- 新增缓存预热服务:启动时预热仪表盘统计、热力图、每日统计
- 优化缓存 TTL 配置:仪表盘统计 2 分钟、热力图和每日统计 10 分钟
- 自动刷新间隔从 10 秒调整为 5 秒
This commit is contained in:
fawney19
2026-01-15 11:45:46 +08:00
parent 24b65d76b5
commit 514bf7e3ed
15 changed files with 386 additions and 39 deletions

View File

@@ -9,6 +9,7 @@ from typing import List, Optional
from sqlalchemy.orm import Session
from src.core.batch_committer import get_batch_committer
from src.core.logger import logger
from src.models.database import RequestCandidate
@@ -289,3 +290,40 @@ class RequestCandidateService:
"available_count": available_count, # 新增:尚未被调度的候选数
"failure_rate": round(failure_rate, 2),
}
@staticmethod
def calculate_candidate_ttfb(
db: Session,
candidate_id: str,
request_start_time: float,
global_first_byte_time_ms: int,
) -> int:
"""
计算候选自身的首字节时间 (TTFB)
请求链路追踪中的 TTFB 应该是"该候选自身"的首字时间,
而不是整个请求从开始到收到首字节的时间。
Args:
db: 数据库会话
candidate_id: 候选 ID
request_start_time: 请求开始时间Unix timestamp
global_first_byte_time_ms: 全局首字节时间(相对于 request_start_time 的毫秒数)
Returns:
候选自身的 TTFB毫秒如果计算失败则返回 global_first_byte_time_ms
"""
try:
candidate = db.query(RequestCandidate).filter(RequestCandidate.id == candidate_id).first()
if candidate and candidate.started_at:
started_at = candidate.started_at
if started_at.tzinfo is None:
started_at = started_at.replace(tzinfo=timezone.utc)
# 使用整数毫秒计算,避免浮点精度问题
request_start_epoch_ms = round(request_start_time * 1000)
started_at_epoch_ms = round(started_at.timestamp() * 1000)
first_byte_epoch_ms = request_start_epoch_ms + global_first_byte_time_ms
return max(0, int(first_byte_epoch_ms - started_at_epoch_ms))
except Exception as e:
logger.debug(f"计算候选 TTFB 失败: {e}")
return global_first_byte_time_ms