# 设计文档:ETL 管道全流程调试与 Debug ## 概述 本设计覆盖 `apps/etl/pipelines/feiqiu/` 下 ETL 管道的全流程调试,采用五阶段策略: 1. **分层单元调试**:逐层(ODS → DWD → DWS → INDEX)使用 FakeDB/FakeAPI 和真实数据库验证任务逻辑 2. **全量数据刷新**:执行 2026-01-01 至 2026-02-16 的 `api_full` Flow,内嵌性能计时,边执行边 Debug,发现问题即修复并重试 3. **黑盒数据校验**:从 API 源数据出发,逐层对比各 Schema 各表的记录数、金额汇总、可疑值检测和抽样逐字段比对 4. **架构优化分析**:分析 ETL 整体结构,在保证稳定和正确的基础上提出精简架构的建议,生成架构报告 5. **报告生成**:基于全量刷新阶段采集的计时数据生成性能分析报告,汇总所有调试结果生成 Debug 报告 性能计时从全量刷新阶段开始就嵌入采集(每任务/每层/API 调用耗时),报告阶段仅做分析和输出,不再单独重跑流程采集数据。 调试使用 `.env` 中配置的真实 API 和数据库连接(test_etl_feiqiu),所有发现的问题和修复记录在结构化 Debug 报告中。性能计时在全量刷新阶段实时采集,报告阶段读取中间数据做分析输出。 ## 架构 ### 调试流程架构 ```mermaid flowchart TD A[阶段1: 分层单元调试] --> B[阶段2: 全量数据刷新
内嵌性能计时] B --> C[阶段3: 黑盒数据校验] C --> D[阶段4: 架构优化分析] D --> E[阶段5: 报告生成
含性能分析] subgraph 阶段1 [分层单元调试] A1[ODS 层: API抓取 → 表写入] --> A2[DWD 层: ODS → DWD 清洗装载] A2 --> A3[DWS 层: DWD → DWS 汇总] A3 --> A4[INDEX 层: 指数算法计算] A4 --> A5[编排层: PipelineRunner + TaskExecutor] A5 --> A6[CLI 入口: 参数解析 + 模式切换] A6 --> A7[配置体系: AppConfig 加载合并] end subgraph 阶段2 [全量数据刷新 - 内嵌计时 + 边执行边Debug] B1[api_full Flow 执行
每任务/每层计时] --> B2{发现Bug?} B2 -->|是| B3[修复Bug] B3 --> B4[重试失败的层/任务] B4 --> B2 B2 -->|否| B5[increment_verify 校验] B5 --> B6[自动补齐缺失数据] B6 --> B7[输出计时 JSON 中间文件] end subgraph 阶段3 [黑盒数据校验] C1[API → ODS 记录数对比] --> C2[ODS → DWD 记录数+金额对比] C2 --> C3[DWD → DWS 聚合一致性] C3 --> C4[可疑值检测: 边缘值/空值/重复] C4 --> C5[抽样100条逐字段比对] end subgraph 阶段4 [架构优化分析] D1[代码结构分析] --> D2[冗余识别] D2 --> D3[精简建议] end subgraph 阶段5 [报告生成 - 含性能分析] E1[读取计时 JSON] --> E2[瓶颈识别 + 优化建议] E2 --> E3[汇总 Debug 报告] end ``` ### 现有系统架构 ```mermaid flowchart LR CLI[cli/main.py] --> PR[PipelineRunner] CLI --> TE[TaskExecutor] PR --> TE TE --> TR[TaskRegistry
52个任务] TE --> CM[CursorManager] TE --> RT[RunTracker] TR --> ODS[ODS 任务
BaseOdsTask × 23] TR --> DWD[DWD 任务
DwdLoadTask] TR --> DWS[DWS 任务
BaseDwsTask × 15] TR --> IDX[INDEX 任务 × 4] TR --> UTL[工具类任务 × 7] ODS --> API[APIClient
上游 SaaS] ODS --> DB[(PostgreSQL
ods.*)] DWD --> DB DWS --> DB IDX --> DB QC[IntegrityChecker] --> API QC --> DB ``` ## 组件与接口 ### 调试脚本组件 调试通过一组 Python 脚本实现,放置在 `apps/etl/pipelines/feiqiu/scripts/debug/` 目录下: | 脚本 | 职责 | 对应需求 | |------|------|----------| | `debug_ods.py` | ODS 层逐任务调试:连接真实 API 和 DB,执行单个 ODS 任务并验证写入结果 | 需求 1, 9 | | `debug_dwd.py` | DWD 层调试:执行 DWD_LOAD_FROM_ODS 并验证 TABLE_MAP 中每对映射的记录数 | 需求 2, 9 | | `debug_dws.py` | DWS 层调试:逐个执行 DWS 任务并验证汇总结果 | 需求 3, 9 | | `debug_index.py` | INDEX 层调试:执行 4 个指数任务并验证计算结果 | 需求 4, 9 | | `debug_orchestration.py` | 编排层调试:验证 PipelineRunner 和 TaskExecutor 的流程控制 | 需求 5 | | `run_full_refresh.py` | 全量刷新:执行 2026-01-01 ~ 2026-02-16 的 api_full,内嵌性能计时,边执行边 Debug,发现问题即修复重试,输出计时 JSON | 需求 10, 13 | | `debug_blackbox.py` | 黑盒校验:从 API 源数据逐层对比各表数据完整性 + 可疑值检测 + 抽样逐字段比对 | 需求 12 | | `analyze_performance.py` | 性能分析:读取全量刷新采集的计时 JSON,统计耗时,识别瓶颈,生成性能优化报告 | 需求 13 | | `analyze_architecture.py` | 架构分析:分析代码结构,识别冗余和可精简点,生成架构优化报告 | 需求 14 | | `generate_report.py` | 报告生成:汇总所有调试结果生成 Markdown 报告 | 需求 11 | ### 关键接口 ```python # 调试脚本的统一入口接口 class DebugResult: """单个调试步骤的结果""" layer: str # "ODS" / "DWD" / "DWS" / "INDEX" / "ORCHESTRATION" task_code: str # 任务代码 status: str # "PASS" / "FAIL" / "WARN" / "ERROR" message: str # 结果描述 details: dict # 详细信息(记录数、错误堆栈等) fix_applied: str | None # 已应用的修复措施 class BlackboxCheckResult: """黑盒校验单表结果""" layer: str # "API_ODS" / "ODS_DWD" / "DWD_DWS" source_table: str # 源表名 target_table: str # 目标表名 source_count: int # 源记录数 target_count: int # 目标记录数 count_diff: int # 差异数 amount_diffs: list # 金额差异列表 missing_keys: list # 缺失记录主键 mismatch_count: int # 内容不一致数 ``` ## 数据模型 ### Debug 报告结构 ```python @dataclass class DebugReport: """Debug 报告数据模型""" title: str # 报告标题 generated_at: datetime # 生成时间 environment: dict # 环境信息(DB DSN, API base, store_id) # 分层调试结果 ods_results: list[DebugResult] # ODS 层调试结果 dwd_results: list[DebugResult] # DWD 层调试结果 dws_results: list[DebugResult] # DWS 层调试结果 index_results: list[DebugResult] # INDEX 层调试结果 orchestration_results: list[DebugResult] # 编排层调试结果 # 黑盒校验结果 blackbox_results: list[BlackboxCheckResult] # 全量刷新结果 full_refresh: dict # {status, layers, counts, duration} verification: dict # {status, total_tables, consistent, backfilled} # 性能分析 performance_report: dict # {layer_timings, bottlenecks, recommendations} # 架构优化 architecture_report: dict # {structure_analysis, redundancies, simplification_suggestions} # 汇总 total_issues: int # 发现的问题总数 fixed_issues: int # 已修复的问题数 remaining_issues: int # 遗留问题数 ``` ### 黑盒校验数据流 ```mermaid flowchart TD API[上游 API] -->|iter_paginated| COUNT_API[API 记录数] API -->|抽样100条| SAMPLE[抽样记录集] ODS[(ods.* 表)] -->|SELECT COUNT| COUNT_ODS[ODS 记录数] ODS -->|可疑值检测| SUSPECT[可疑值: 边缘值/空值/重复] DWD[(dwd.* 表)] -->|SELECT COUNT| COUNT_DWD[DWD 记录数] DWS[(dws.* 表)] -->|SELECT SUM| SUM_DWS[DWS 汇总值] COUNT_API --> CMP1{API vs ODS
记录数对比} COUNT_ODS --> CMP1 COUNT_ODS --> CMP2{ODS vs DWD
记录数+金额对比} COUNT_DWD --> CMP2 COUNT_DWD --> CMP3{DWD vs DWS
聚合一致性} SUM_DWS --> CMP3 SAMPLE --> CMP4{抽样逐字段比对
API vs ODS 100条} ODS --> CMP4 SUSPECT --> CMP5{可疑值分析
追溯上游原因} CMP1 --> RPT[校验报告] CMP2 --> RPT CMP3 --> RPT CMP4 --> RPT CMP5 --> RPT ``` ## 正确性属性 *属性是在系统所有有效执行中都应成立的特征或行为——本质上是关于系统应该做什么的形式化陈述。属性是人类可读规范与机器可验证正确性保证之间的桥梁。* 基于前置分析,以下属性覆盖了可自动化测试的验收标准。本项目的许多需求(特别是需求 8-12 的黑盒校验和全量刷新)依赖真实数据库和 API 连接,属于集成测试范畴,不适合属性测试。属性测试聚焦于可用 FakeDB/FakeAPI 验证的核心逻辑。 ### Property 1: ODS 任务提取记录数一致性 *对任意* ODS 任务和 FakeAPI 提供的任意非空记录列表,任务执行后 FakeDB 接收到的记录数应等于 API 提供的记录数减去被跳过的记录数(缺失主键 + 重复哈希)。 **Validates: Requirements 1.1, 1.2** ### Property 2: ODS 冲突处理策略正确性 *对任意* `ods_conflict_mode` 配置值(nothing/backfill/update),BaseOdsTask 生成的 SQL 语句应包含对应的冲突处理子句:nothing → `DO NOTHING`,backfill → `COALESCE`,update → `IS DISTINCT FROM`。 **Validates: Requirements 1.3** ### Property 3: ODS 跳过缺失主键记录 *对任意*包含缺失主键字段的记录集合,BaseOdsTask 的 skipped 计数应等于缺失主键的记录数,且这些记录不应出现在写入的行中。 **Validates: Requirements 1.4** ### Property 4: ODS content_hash 去重 *对任意*两条内容相同的 ODS 记录(仅 fetched_at 不同),计算出的 content_hash 应相同;当已有记录的 content_hash 与新记录相同时,新记录应被跳过。 **Validates: Requirements 1.5** ### Property 5: ODS 快照删除标记 *对任意*启用 `snapshot_missing_delete` 的 ODS 任务,当 API 返回的记录集是已有记录集的真子集时,差集中的记录应被标记为 `is_delete=1`。 **Validates: Requirements 1.7** ### Property 6: DWD FACT_MAPPINGS 列映射完整性 *对任意* FACT_MAPPINGS 中的映射条目 `(dwd_col, ods_expr, cast_type)`,当 `ods_expr` 是简单列名时,该列应存在于对应的 ODS 源表中。 **Validates: Requirements 2.4** ### Property 7: DWD only_tables 过滤 *对任意*非空的 `dwd.only_tables` 配置列表,DwdLoadTask 处理的表集合应是配置列表与 TABLE_MAP 键集合的交集。 **Validates: Requirements 2.6** ### Property 8: DWS 分段累加一致性 *对任意*时间窗口和分段配置,BaseTask 的 `_accumulate_counts` 方法对各分段计数的累加结果应等于各分段计数的逐键求和。 **Validates: Requirements 3.3** ### Property 9: PipelineRunner Flow 层解析 *对任意*有效的 Flow 名称(PIPELINE_LAYERS 的键),PipelineRunner 解析出的层列表应与 PIPELINE_LAYERS 中定义的值完全一致。 **Validates: Requirements 5.1, 10.2** ### Property 10: PipelineRunner 无效 Flow 拒绝 *对任意*不在 PIPELINE_LAYERS 键集合中的字符串,PipelineRunner.run() 应抛出 ValueError。 **Validates: Requirements 5.2** ### Property 11: TaskExecutor 工具类任务跳过游标 *对任意*被 TaskRegistry 标记为 `requires_db_config=False` 的任务代码,TaskExecutor 应通过 `_run_utility_task` 路径执行,不调用 CursorManager 和 RunTracker。 **Validates: Requirements 5.5** ### Property 12: CLI data_source 解析 *对任意* `--data-source` 参数值(online/offline/hybrid)和 `--pipeline-flow` 参数值(FULL/FETCH_ONLY/INGEST_ONLY),`resolve_data_source` 应返回正确的映射值,且 `--data-source` 优先于 `--pipeline-flow`。 **Validates: Requirements 6.3, 6.4** ### Property 13: AppConfig 优先级合并 *对任意*嵌套字典 DEFAULTS 和 CLI 覆盖,`_deep_merge` 后 CLI 中的键值应覆盖 DEFAULTS 中的同名键值,未被覆盖的键应保持原值。 **Validates: Requirements 7.1** ### Property 14: AppConfig store_id 验证 *对任意*非整数字符串作为 `app.store_id`,AppConfig._normalize 应抛出 SystemExit。 **Validates: Requirements 7.2** ### Property 15: AppConfig DSN 组装 *对任意* host、port、name、user、password 组合(db.dsn 为空时),AppConfig._normalize 应组装出格式为 `postgresql://{user}:{password}@{host}:{port}/{name}` 的 DSN 字符串。 **Validates: Requirements 7.3** ### Property 16: AppConfig 点号路径 get *对任意*嵌套字典和有效的点号路径,`config.get(path)` 应返回路径对应的值;对无效路径应返回 default 参数值。 **Validates: Requirements 7.4** ## 错误处理 ### 数据库连接错误 - 调试脚本在启动时验证数据库连接,连接失败时记录错误详情并在报告中标注 - 单表处理失败时回滚该表事务,继续处理后续表(DWD 层已实现此逻辑) - 全量刷新过程中连接断开时,利用 `DatabaseConnection.ensure_open()` 尝试重连 ### API 连接错误 - APIClient 内置重试机制(`retry_max=3`,指数退避) - API 返回非 0 code 时抛出 ValueError,由上层捕获并记录 - Token 过期时在报告中标注,提示用户更新 `.env` 中的 `API_TOKEN` ### 任务执行错误 - ODS 任务:数据库异常时回滚并递增 errors 计数 - DWD 任务:单表失败时回滚该表,继续后续表,最终汇总错误 - DWS/INDEX 任务:继承 BaseTask 的异常处理,回滚后重新抛出 - 工具类任务:异常直接向上传播,不影响游标和运行记录 ### 数据质量错误 - 黑盒校验发现的不一致记录在报告中详细列出主键 - 金额差异超过阈值时标记为 WARN 级别 - 校验后自动补齐通过 `run_backfill` 执行,补齐失败的记录单独记录 ## 测试策略 ### 双轨测试方法 本项目采用单元测试 + 属性测试的双轨方法: - **属性测试**:使用 `hypothesis` 库验证上述 16 个正确性属性,每个属性至少运行 100 次迭代 - **单元测试**:使用 `pytest` 验证具体示例、边界情况和错误条件 - **集成测试**:使用真实数据库(`TEST_DB_DSN`)验证端到端数据流 ### 属性测试配置 - 库:`hypothesis`(已在项目依赖中) - 最小迭代次数:100 - 每个属性测试必须引用设计文档中的属性编号 - 标签格式:`Feature: etl-pipeline-debug, Property {number}: {property_text}` ### 测试文件组织 ``` apps/etl/pipelines/feiqiu/tests/unit/ ├── test_debug_ods_properties.py # Property 1-5: ODS 层属性测试 ├── test_debug_dwd_properties.py # Property 6-8: DWD/DWS 层属性测试 ├── test_debug_orchestration_properties.py # Property 9-12: 编排层属性测试 ├── test_debug_config_properties.py # Property 13-16: 配置层属性测试 └── (现有测试文件保持不变) ``` ### 集成测试 集成测试通过调试脚本实现,连接真实数据库和 API: - `debug_ods.py`:逐个执行 ODS 任务,验证写入结果 - `debug_dwd.py`:执行 DWD 装载,验证映射正确性 - `debug_blackbox.py`:黑盒校验,逐层对比数据完整性 - `run_full_refresh.py`:全量刷新 + 校验 ### 现有测试基础设施 项目已有完善的测试工具: - `FakeDBOperations`:拦截并记录 SQL 操作,提供 commit/rollback 计数 - `FakeAPIClient`:返回预置内存数据,记录调用参数 - `OfflineAPIClient`:从归档 JSON 回放数据 - `RealDBOperationsAdapter`:连接真实 PostgreSQL 的适配器 - `create_test_config()`:构建测试用 AppConfig