微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
229
.kiro/specs/etl-unified-pipeline/tasks.md
Normal file
229
.kiro/specs/etl-unified-pipeline/tasks.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# 实施计划:ETL 统一请求编排与线程模型改造
|
||||
|
||||
## 概述
|
||||
|
||||
将飞球 Connector ETL 系统的 ODS 任务从同步串行执行迁移到"串行请求 + 异步处理 + 单线程写库"统一管道架构。按组件依赖顺序逐步实现:基础组件 → 核心引擎 → 任务迁移 → DWD 优化 → 日志优化。
|
||||
|
||||
## 任务
|
||||
|
||||
- [x] 1. 实现基础组件(PipelineConfig、CancellationToken、RateLimiter)
|
||||
- [x] 1.1 创建 `apps/etl/connectors/feiqiu/config/pipeline_config.py`,实现 `PipelineConfig` 数据类
|
||||
- 定义 `workers`、`queue_size`、`batch_size`、`batch_timeout`、`rate_min`、`rate_max`、`max_consecutive_failures` 字段及默认值
|
||||
- 实现 `__post_init__` 参数校验(workers>=1、queue_size>=1、batch_size>=1、rate_min<=rate_max)
|
||||
- 实现 `from_app_config(config, task_code)` 类方法,支持 `pipeline.<task_code>.*` 任务级覆盖 → 全局 `pipeline.*` → 默认值的三级回退
|
||||
- _需求: 1.3, 1.4, 1.5, 2.3, 2.5, 2.6, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6_
|
||||
|
||||
- [x] 1.2 编写 PipelineConfig 属性测试
|
||||
- **Property 3: PipelineConfig 构造与验证** — 生成随机配置参数组合(含无效值),验证合法参数成功构造、非法参数抛出 ValueError
|
||||
- **Property 4: 配置分层与任务级覆盖** — 生成随机多层配置字典,验证任务级 > 全局级 > 默认值的优先级
|
||||
- 测试文件:`tests/test_pipeline_config_properties.py`
|
||||
- **验证: 需求 1.4, 1.5, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6**
|
||||
|
||||
- [x] 1.3 创建 `apps/etl/connectors/feiqiu/utils/cancellation.py`,实现 `CancellationToken` 类
|
||||
- 基于 `threading.Event` 实现线程安全的 `cancel()` 方法和 `is_cancelled` 属性
|
||||
- 实现超时自动取消(构造时传入 `timeout` 秒数,通过 `threading.Timer` 触发)
|
||||
- 实现 `dispose()` 清理定时器
|
||||
- _需求: 3.1, 3.6_
|
||||
|
||||
- [x] 1.4 编写 CancellationToken 属性测试
|
||||
- **Property 7: CancellationToken 状态转换** — 生成随机超时值,验证初始 False、cancel() 后 True 且不可逆、超时自动触发
|
||||
- 测试文件:`tests/test_cancellation_properties.py`
|
||||
- **验证: 需求 3.1, 3.6**
|
||||
|
||||
- [x] 1.5 创建 `apps/etl/connectors/feiqiu/api/rate_limiter.py`,实现 `RateLimiter` 类
|
||||
- 构造时校验 `min_interval <= max_interval`,否则抛出 `ValueError`
|
||||
- 实现 `wait(cancel_event)` 方法:生成 `[min, max]` 均匀分布随机间隔,拆分为 0.5s 小段轮询 cancel_event
|
||||
- 暴露 `last_interval` 属性
|
||||
- _需求: 1.3, 1.5_
|
||||
|
||||
- [x] 1.6 编写 RateLimiter 属性测试
|
||||
- **Property 2: RateLimiter 间隔范围** — 生成随机 (min, max) 对,验证 wait() 实际等待时间在 [min, max] ± 0.5s 范围内
|
||||
- 测试文件:`tests/test_rate_limiter_properties.py`
|
||||
- **验证: 需求 1.3**
|
||||
|
||||
- [x] 1.7 编写基础组件单元测试
|
||||
- 测试文件:`apps/etl/connectors/feiqiu/tests/unit/test_pipeline_config.py`、`tests/unit/test_cancellation.py`、`tests/unit/test_rate_limiter.py`
|
||||
- 覆盖边界条件:RateLimiter min=max、CancellationToken 预取消/timeout=0/dispose、PipelineConfig 无效参数/CLI 覆盖
|
||||
- _需求: 1.3, 1.5, 3.1, 3.6, 4.1, 4.4, 4.5_
|
||||
|
||||
- [x] 2. 检查点 — 基础组件验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 3. 实现核心管道引擎(UnifiedPipeline)
|
||||
- [x] 3.1 创建数据类 `PipelineRequest`、`PipelineResult`、`WriteResult`
|
||||
- 文件:`apps/etl/connectors/feiqiu/pipeline/models.py`
|
||||
- `PipelineRequest`:endpoint、params、page_size、data_path、list_key、segment_index、is_detail、detail_id
|
||||
- `PipelineResult`:status、各阶段计数、errors 列表、timing 字典、Detail_Mode 统计
|
||||
- `WriteResult`:inserted、updated、skipped、errors
|
||||
- _需求: 2.7, 6.6, 8.2, 9.4_
|
||||
|
||||
- [x] 3.2 创建 `apps/etl/connectors/feiqiu/pipeline/unified_pipeline.py`,实现 `UnifiedPipeline` 核心引擎
|
||||
- 实现 `__init__`:接收 api_client、db_connection、logger、PipelineConfig、CancellationToken,初始化 RateLimiter
|
||||
- 实现 `run(requests, process_fn, write_fn) -> PipelineResult` 主方法:
|
||||
- 预取消检查(cancel_token 已取消则立即返回空结果)
|
||||
- 创建 processing_queue(maxsize=queue_size)和 write_queue(maxsize=queue_size*2)
|
||||
- 启动 N 个 worker 线程(`_process_worker`)和 1 个 writer 线程(`_write_worker`)
|
||||
- 主线程执行 `_request_loop`:串行发送请求、限流等待、取消检查、背压阻塞
|
||||
- 发送 SENTINEL 通知线程退出,join 等待完成
|
||||
- 计算最终 status(SUCCESS/PARTIAL/CANCELLED/FAILED)
|
||||
- _需求: 1.1, 1.2, 1.6, 2.1, 2.2, 2.4, 2.7, 2.8, 3.2, 3.7, 8.3_
|
||||
|
||||
- [x] 3.3 实现 `_request_loop` 请求调度逻辑
|
||||
- 遍历 requests 迭代器,逐个发送 API 请求
|
||||
- 每个请求完成后记录耗时、状态码、endpoint 到日志
|
||||
- 将响应数据 put 到 processing_queue(满时阻塞 = 背压)
|
||||
- 请求间调用 rate_limiter.wait(cancel_event),被取消则 break
|
||||
- 实现连续失败计数:成功重置为 0,失败 +1,超过 max_consecutive_failures 则中断
|
||||
- _需求: 1.2, 1.7, 3.2, 8.1, 8.3, 9.1, 9.5_
|
||||
|
||||
- [x] 3.4 实现 `_process_worker` 处理线程逻辑
|
||||
- 从 processing_queue 消费数据,调用 process_fn 处理
|
||||
- 处理结果 put 到 write_queue
|
||||
- 单条记录处理异常时捕获、记录错误、标记失败、继续处理
|
||||
- 收到 SENTINEL 时退出
|
||||
- _需求: 2.1, 2.2, 9.2_
|
||||
|
||||
- [x] 3.5 实现 `_write_worker` 写入线程逻辑
|
||||
- 从 write_queue 消费数据,累积到 batch_size 或等待 batch_timeout 后调用 write_fn 批量写入
|
||||
- 写入失败时回滚当前批次事务、记录错误、标记失败、继续处理后续批次
|
||||
- 队列积压超过 queue_size*2 时记录警告日志
|
||||
- 收到 SENTINEL 时将剩余数据 flush 写入后退出
|
||||
- _需求: 2.4, 2.5, 2.6, 8.4, 9.3, 9.6_
|
||||
|
||||
- [x] 3.6 编写 UnifiedPipeline 属性测试
|
||||
- **Property 1: 请求严格串行** — 用 FakeAPI 记录时间戳,验证每个请求发送时间 > 上一个响应完成时间
|
||||
- **Property 5: 管道完成语义** — 生成随机记录集,验证 total_fetched == total_inserted + total_updated + total_skipped
|
||||
- **Property 6: WriteWorker 批量大小约束** — 生成随机 batch_size 和记录流,验证每次 write_fn 调用的记录数 <= batch_size
|
||||
- **Property 8: 取消后已入队数据不丢失** — 生成随机请求序列 + 随机取消时刻,验证已入队数据全部处理和写入
|
||||
- **Property 11: 单项失败不中断整体** — 生成含随机失败的请求序列,验证后续项目继续处理
|
||||
- **Property 12: 连续失败触发中断** — 生成连续失败序列 + 随机阈值,验证超过阈值时中断
|
||||
- **Property 13: 写入失败回滚当前批次** — 生成含随机写入失败的批次,验证回滚且后续批次不受影响
|
||||
- **Property 14: 结果统计完整性** — 验证各计数字段的一致性关系
|
||||
- 测试文件:`tests/test_pipeline_properties.py`
|
||||
- **验证: 需求 1.2, 1.6, 2.5, 2.7, 3.2, 3.3, 3.4, 3.5, 6.6, 8.2, 9.1, 9.2, 9.3, 9.4, 9.5**
|
||||
|
||||
- [x] 3.7 编写 UnifiedPipeline 单元测试
|
||||
- 测试文件:`apps/etl/connectors/feiqiu/tests/unit/test_unified_pipeline.py`
|
||||
- 使用 FakeAPI + FakeDB 端到端测试:正常流程、空请求、预取消、背压触发
|
||||
- _需求: 2.7, 3.7, 8.1, 8.3_
|
||||
|
||||
- [x] 4. 检查点 — 核心引擎验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 5. BaseOdsTask 改造与 ODS 任务迁移
|
||||
- [x] 5.1 扩展 `OdsTaskSpec` 数据类,新增 Detail_Mode 可选字段
|
||||
- 在 `apps/etl/connectors/feiqiu/tasks/ods/ods_tasks.py` 中为 `OdsTaskSpec` 新增:`detail_endpoint`、`detail_param_builder`、`detail_target_table`、`detail_data_path`、`detail_list_key`、`detail_id_column`
|
||||
- 所有新增字段默认值为 `None`,不影响现有 21 个任务的 OdsTaskSpec 实例
|
||||
- _需求: 6.2, 6.3_
|
||||
|
||||
- [x] 5.2 改造 `BaseOdsTask.execute()` 方法,嵌入 UnifiedPipeline
|
||||
- 在 `execute()` 内部构建 `PipelineConfig.from_app_config(self.config, spec.code)`
|
||||
- 将现有分页请求逻辑封装为 `_build_requests()` → `Iterable[PipelineRequest]`
|
||||
- 将现有字段提取/hash 计算封装为 `_build_process_fn()` → `Callable`
|
||||
- 将现有 `_insert_records_schema_aware` 封装为 `_build_write_fn()` → `Callable`
|
||||
- 调用 `pipeline.run(requests, process_fn, write_fn)` 替代现有同步循环
|
||||
- 保留快照软删除(`_mark_missing_as_deleted`)、endpoint_routing、元数据写入(source_file、source_endpoint、fetched_at)
|
||||
- 保留 TaskExecutor 调用接口不变(`task.execute(cursor_data)` 签名不变)
|
||||
- _需求: 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7_
|
||||
|
||||
- [x] 5.3 实现 Detail_Mode 详情拉取逻辑
|
||||
- 在 `BaseOdsTask` 中实现 `_build_detail_requests()` 方法:从已写入 ODS 的记录中提取 ID 列表,生成 `PipelineRequest(is_detail=True)` 序列
|
||||
- 详情请求通过同一个 UnifiedPipeline 的 RequestScheduler 排队,遵循相同限流规则
|
||||
- 单个详情请求失败时记录错误日志(含 ID 和错误信息),继续处理下一个
|
||||
- 在 PipelineResult 中填充 detail_success/detail_failure/detail_skipped 统计
|
||||
- _需求: 6.1, 6.4, 6.5, 6.6_
|
||||
|
||||
- [x] 5.4 编写 Detail_Mode 属性测试
|
||||
- **Property 10: Detail_Mode 可选性** — 生成有/无 detail_endpoint 的 OdsTaskSpec,验证无配置时跳过详情阶段、有配置时执行详情拉取且遵循限流
|
||||
- 测试文件:`tests/test_detail_mode_properties.py`
|
||||
- **验证: 需求 6.1, 6.3, 6.4**
|
||||
|
||||
- [x] 5.5 编写迁移等价属性测试
|
||||
- **Property 9: 迁移前后输出等价** — 生成随机 API 响应序列,对比 UnifiedPipeline 与原同步串行实现的数据库写入结果(inserted/updated/skipped 计数和记录内容)
|
||||
- 测试文件:`tests/test_migration_properties.py`
|
||||
- **验证: 需求 5.1, 5.3, 5.4, 5.5**
|
||||
|
||||
- [x] 5.6 编写 Detail_Mode 和迁移单元测试
|
||||
- 测试文件:`apps/etl/connectors/feiqiu/tests/unit/test_detail_mode.py`
|
||||
- 覆盖:列表→详情完整流程、无 detail_endpoint 跳过、详情单条失败不中断
|
||||
- _需求: 6.1, 6.3, 6.5_
|
||||
|
||||
- [x] 6. 检查点 — ODS 迁移验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 7. DWD 层多线程优化
|
||||
- [x] 7.1 改造 `apps/etl/connectors/feiqiu/tasks/dwd/dwd_load_task.py` 中的 `DwdLoadTask.load()` 方法
|
||||
- 从 `AppConfig` 读取 `dwd.parallel_workers`(默认 4)
|
||||
- 将现有串行 `for dwd_table, ods_table in TABLE_MAP` 循环改为 `concurrent.futures.ThreadPoolExecutor` 并行调度
|
||||
- 每张表调用 `_process_single_table()` 在独立线程中执行,使用独立数据库连接和事务
|
||||
- `_merge_dim_scd2()` 方法本身不改
|
||||
- 单张表失败时捕获异常、记录错误日志(含表名和错误信息)、标记失败、继续处理其他表
|
||||
- 所有表处理完成后返回汇总结果:成功表数、失败表数、每张表的 inserted/updated 计数
|
||||
- _需求: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6_
|
||||
|
||||
- [x] 7.2 编写 DWD 并行属性测试
|
||||
- **Property 16: DWD 并行与串行结果一致** — 生成随机表集合 + mock SCD2,验证多线程并行执行的结果与串行逐表执行完全一致
|
||||
- 测试文件:`tests/test_dwd_parallel_properties.py`
|
||||
- **验证: 需求 7.1**
|
||||
|
||||
- [x] 7.3 编写 DWD 多线程单元测试
|
||||
- 测试文件:`apps/etl/connectors/feiqiu/tests/unit/test_dwd_parallel.py`
|
||||
- 覆盖:mock SCD2 正常并行、单表失败不影响其他表、汇总结果正确
|
||||
- _需求: 7.3, 7.4, 7.5_
|
||||
|
||||
- [x] 8. 可观测性与日志优化
|
||||
- [x] 8.1 在 UnifiedPipeline 中集成运行时指标日志
|
||||
- 在 `_request_loop` 中定期记录:当前请求队列深度、ProcessingPool 活跃线程数、WriteWorker 待写入队列深度、已完成请求数/总请求数
|
||||
- 在 `run()` 返回前计算并记录执行摘要:总耗时、请求/处理/写入各阶段耗时、各阶段记录数统计
|
||||
- 与现有 `EtlTimer` 集成,在 FlowRunner 计时报告中体现各 ODS 任务的阶段耗时
|
||||
- _需求: 8.1, 8.2, 8.5_
|
||||
|
||||
- [x] 8.2 创建 `apps/etl/connectors/feiqiu/utils/task_log_buffer.py`,实现 `TaskLogBuffer` 类
|
||||
- 实现线程安全的 `log(level, message)` 方法,将日志条目缓冲到内存列表
|
||||
- 实现 `flush()` 方法:按时间戳升序排列,一次性输出到父 logger,添加 `[task_code]` 前缀
|
||||
- 定义 `LogEntry` 数据类(timestamp、level、task_code、message)
|
||||
- _需求: 10.1, 10.3, 10.4_
|
||||
|
||||
- [x] 8.3 编写日志缓冲区属性测试
|
||||
- **Property 15: 日志缓冲区按任务隔离** — 生成多任务随机日志流,验证每个 TaskLogBuffer 的 flush() 仅包含该任务日志且按时间戳升序
|
||||
- 测试文件:`tests/test_log_buffer_properties.py`
|
||||
- **验证: 需求 10.1, 10.4**
|
||||
|
||||
- [x] 8.4 编写 TaskLogBuffer 单元测试
|
||||
- 测试文件:`apps/etl/connectors/feiqiu/tests/unit/test_task_log_buffer.py`
|
||||
- 覆盖:空缓冲区 flush、并发多线程写入、日志前缀格式
|
||||
- _需求: 10.1, 10.3, 10.4_
|
||||
|
||||
- [x] 9. 检查点 — DWD 优化与日志验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
- [x] 10. Admin-web 日志展示优化
|
||||
- [x] 10.1 在 `apps/etl/connectors/feiqiu/` 中集成 TaskLogBuffer 到 BaseOdsTask 和 FlowRunner
|
||||
- 在 BaseOdsTask.execute() 中创建 TaskLogBuffer 实例,替代直接 logger 调用
|
||||
- 在 FlowRunner 中为每个任务分配独立的 TaskLogBuffer,任务完成后调用 flush()
|
||||
- 保证多线程环境下日志写入原子性(每条日志完整一行)
|
||||
- _需求: 10.1, 10.3, 10.4_
|
||||
|
||||
- [x] 10.2 在 `apps/admin-web/` 中实现按任务分组的日志展示
|
||||
- 在 ETL 执行结果页面中按任务分段展示日志:每个任务折叠为独立区块
|
||||
- 展开后显示该任务的完整执行日志(时间戳、日志级别、消息内容)
|
||||
- 支持按任务代码过滤和分组展示
|
||||
- 顶部展示任务执行时间线概览(每个任务的开始/结束时间、状态),可点击跳转
|
||||
- _需求: 10.2, 10.5, 10.6_
|
||||
|
||||
- [x] 11. CLI 参数扩展
|
||||
- [x] 11.1 在 `apps/etl/connectors/feiqiu/cli/` 中添加 Pipeline 相关 CLI 参数
|
||||
- 新增 `--pipeline-workers`、`--pipeline-batch-size`、`--pipeline-rate-min`、`--pipeline-rate-max` 参数
|
||||
- 将 CLI 参数值注入到 AppConfig,使其在 PipelineConfig.from_app_config() 中生效
|
||||
- _需求: 4.6_
|
||||
|
||||
- [x] 12. 最终检查点 — 全量验证
|
||||
- 确保所有测试通过,ask the user if questions arise.
|
||||
|
||||
## 备注
|
||||
|
||||
- 标记 `*` 的子任务为可选,可跳过以加速 MVP 交付
|
||||
- 每个任务引用了具体的需求编号,确保可追溯
|
||||
- 检查点任务用于增量验证,确保每个阶段的正确性
|
||||
- 属性测试验证通用正确性属性,单元测试验证具体示例和边界条件
|
||||
- 属性测试位于 Monorepo 级 `tests/` 目录,单元测试位于 ETL 模块内 `tests/unit/`
|
||||
Reference in New Issue
Block a user