# 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 并行执行: ```python 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 获取: ```python # 优化前:需要查 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()` 调用: ```typescript // 修复前 } 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_datasources` - `frontend/src/admin/pages/PlainResourcePages.tsx` — `BuiltInDataSource` interface、`triggerDatasource`