3.6 KiB
3.6 KiB
DataSources 列表接口性能优化
背景
GET /api/v1/datasources 是数据源管理页面的核心接口,响应慢会直接阻塞页面渲染。
优化前的查询链路
_load_datasource_list_context 按顺序执行以下查询:
| 序号 | 函数 | 查询内容 | 瓶颈 |
|---|---|---|---|
| 1 | _load_latest_running_tasks |
collection_tasks 窗口函数,stale check 依赖此结果 | 必须串行 |
| 2 | _load_latest_completed_tasks |
collection_tasks 窗口函数(最近完成任务) | 串行等待 |
| 3 | _load_datasource_data_counts |
COUNT(*) GROUP BY source on collected_data |
最慢,全表扫描 |
| 4 | _load_datasource_endpoint_overrides |
datasource_configs 简单 SELECT | 串行等待 |
第一阶段:并行化
将 2/3/4 三个互不依赖的查询改为 asyncio.gather + 独立 session 并行执行:
async def _fetch_completed():
async with async_session_factory() as s:
return await _load_latest_completed_tasks(s, datasource_ids)
async def _fetch_counts():
async with async_session_factory() as s:
return await _load_datasource_data_counts(s, sources)
async def _fetch_overrides():
async with async_session_factory() as s:
return await _load_datasource_endpoint_overrides(s, sources)
completed_tasks, data_counts, endpoint_overrides = await asyncio.gather(
_fetch_completed(), _fetch_counts(), _fetch_overrides(),
)
注意:SQLAlchemy
AsyncSession不支持在同一 session 上并发,每个协程必须独立开 session。
第二阶段:删除重量级查询
删除 _load_datasource_data_counts
data_count 字段仅用于前端在"最近采集"列显示 (0条) 的边缘提示,不值得为此维持一次 COUNT(*) GROUP BY 全表扫描。
- 前端同步移除
(0条)显示逻辑 - 移除
BuiltInDataSource接口中的data_count字段
删除 _load_latest_completed_tasks
last_status 和 last_run_at 已由 collector 在任务完成时直接更新到 DataSource 模型字段,不需要再 JOIN collection_tasks 获取:
# 优化前:需要查 completed_tasks
last_run_at = datasource.last_run_at or (last_task.completed_at if last_task else None)
last_status = datasource.last_status or (last_task.status if last_task else None)
# 优化后:直接读模型字段
last_run_at = datasource.last_run_at
last_status = datasource.last_status
同步移除 last_records_processed 字段(来源是 completed_tasks,列表不显示此字段)。
优化后的查询链路
datasources SELECT → 主数据,必须
_load_latest_running_tasks → 必须(进行中状态 + stale check)
_load_datasource_endpoint_overrides → 必须(endpoint 覆盖,列表详情和采集器设置需要显示当前有效地址)
3 个查询(原来 5 个),后两个顺序执行(running tasks 先完成用于 stale check,endpoint overrides 轻量)。
前端 triggerDatasource 双调修复
triggerDatasource 中存在双重 fetchData() 调用:
// 修复前
} else {
window.setTimeout(() => { fetchData() }, 800) // 无 task_id 时延迟刷
}
fetchData() // 总是立即刷 → 与上面的延迟刷重叠
// 修复后(二者互斥)
if (res.data.task_id) {
fetchData() // 有 task_id:立即刷一次
} else {
window.setTimeout(fetchData, 800) // 无 task_id:等 800ms 再刷一次
}
相关文件
backend/app/api/v1/datasources.py—_load_datasource_list_context、list_datasourcesfrontend/src/pages/DataSources/DataSources.tsx—BuiltInDataSourceinterface、triggerDatasource