Week06|实验|把 Week3–Week5 链路组织成 Dagster 数据工厂,并完成一次分区回填 + 质量验证 + 运行证据交付

跑出 Week06 的最小资产化数据工厂闭环

这次实验不是做一个孤立 Dagster demo。

你要把 Week03–Week05 的链路组织成最小 asset graph,并完成:

materialize partition -> 制造缺口 -> backfill / replay -> checks -> run evidence -> runbook 交接。

这次实验要完成什么

  • 加载 Week06 data factory blueprint
  • 启动 Docker Compose / devbox / Dagster
  • 验证 Definitions 可加载
  • materialize 一个 daily partition
  • 生成 backfill dry-run plan
  • 运行 5 类 asset checks
  • 生成 run evidence JSON / Markdown
  • 按 runbook 记录一次恢复决策

参考实验时间

按课程统一节奏完成即可。建议预留 90–120 分钟:前半段跑通最小闭环,后半段整理 evidence 与 runbook 交接记录。

本次实验产出

类型 文件
Blueprint docs/blueprints/week06/week06-data-factory-blueprint.md
Asset graph docs/blueprints/week06/week06-asset-graph.md
Partition strategy docs/blueprints/week06/week06-partition-backfill-strategy.md
Evidence schema contracts/run_evidence/week06_run_evidence.schema.json
Backfill plan reports/week06/backfill/*.json
Run evidence reports/week06/run_evidence/*.json
Runbook runbooks/week06-data-factory.md
Lab summary reports/week06/week06_lab_execution_summary.md

先看一张闭环图

flowchart LR
    DEF["Definitions loadable"] --> GRAPH["asset graph"]
    GRAPH --> PART["daily partition"]
    PART --> MAT["materialize"]
    MAT --> GAP["safe failure / gap"]
    GAP --> PLAN["backfill dry-run plan"]
    PLAN --> RUN["backfill / replay"]
    RUN --> CHECK["asset checks"]
    CHECK --> EVD["run evidence"]
    EVD --> RB["runbook handoff"]

0. 环境前提

本实验默认 Docker-first:

# 可执行:已与项目 runbook 对齐
cp infra/env/.env.example infra/env/.env.local

docker compose --env-file infra/env/.env.local -f infra/docker-compose.yml up -d --build

docker compose --profile tools --env-file infra/env/.env.local -f infra/docker-compose.yml run --rm devbox bash

目的:保证 Postgres、MinIO、Dagster 和 devbox 使用同一套 compose / env。

期望输出:服务启动成功,Dagster UI 可打开 http://localhost:3000

常见错误:

  • Docker 和 Podman 同时占用端口:只保留一条线路。
  • .env.local 缺失:先从 infra/env/.env.example 复制。
  • Dagster UI 没看到 Week06:先检查 pipelines/definitions.py 是否能加载。

1. 验证 Week06 foundation

先检查文档和 schema:

# 可执行:已与项目 runbook 对齐
python -m json.tool contracts/run_evidence/week06_run_evidence.schema.json
ls docs/blueprints/week06
ls runbooks/week06-data-factory.md
ls reports/week06

目的:确认 Data Factory v1 的文档、schema、runbook、报告目录已经存在。

验收点:能看到 week06-data-factory-blueprint.mdweek06-asset-graph.mdweek06-partition-backfill-strategy.mdweek06-run-evidence-spec.md

2. 验证 Definitions 可加载

建议 smoke test:

# 可执行:已与项目 runbook 对齐
docker compose --profile tools --env-file infra/env/.env.local -f infra/docker-compose.yml run --rm devbox \
  pytest tests/integration/test_week06_definitions_loadable.py -q

如果这里失败,先修 runtime / import / mount,不要继续做 UI 演示。

怎么看输出:测试通过代表 week06/source/seed_manifestsweek06/factory/manifest_gateweek06/ingestion/raw_ticket_events_partitionedweek06/silver/ticket_fact_partitioned 等资产已注册。

3. 观察 source / manifest

Student Core 当前可以让 manifest_gate 直接读 manifest 文件。你需要记录:

  • manifest path
  • source files
  • batch id
  • expected row count
  • data release id

常见错误:如果 Dagster 容器看不到 docs/contracts/reports,多半是 compose mount 或工作目录不一致;不要靠复制文件绕过,要修 runtime 边界。

4. Materialize 一个 daily partition

选择一个固定 partition,例如:

partition_key = 2026-04-17

记录:

  • 运行方式:UI 或 CLI
  • target assets
  • started_at / finished_at
  • output row count
  • report path

可执行 smoke test:

# 可执行:已与项目 runbook 对齐
docker compose --profile tools --env-file infra/env/.env.local -f infra/docker-compose.yml run --rm devbox \
  pytest tests/integration/test_week06_asset_graph_smoke.py -q

期望输出:测试通过,并生成 reports/week06/run_evidence/ 或临时测试目录下的 evidence 文件。

5. 安全制造缺口

不要危险删除主表。建议选择:

  • 删除某个 report 文件的副本
  • 用 dry-run mode 生成未写下游的记录
  • 构造一个失败 manifest
  • 让某个 check 的输入样本缺少 required field

目的:演示恢复动作,而不是破坏真实数据。

验收点:你能写清 gap reason,例如 missing_partition_reportpartition_has_no_seed_rowsoptional_dependency_not_available

6. 生成 backfill dry-run plan

# 可执行:已与项目 runbook 对齐
docker compose --profile tools --env-file infra/env/.env.local -f infra/docker-compose.yml run --rm devbox \
  python -m pipelines.data_factory.backfill_plan --partition 2026-04-17 --mode dry-run

期望输出:reports/week06/backfill/backfill_plan_2026-04-17.json

怎么看输出:

  • partition_key 应为 2026-04-17
  • proposed_action 应表达 dry-run 的补数动作。
  • idempotency_guard 需要说明如何避免重复 side effect。

常见错误:expected input 为 0 时,先看 seed 数据里有没有该日期;不要马上改代码。

7. 运行 asset checks

至少检查:

Check 目标
manifest consistency manifest 与实际输入一致
row count 输入 / 输出行数合理
duplicate / idempotency 没有重复 side effect
required field null rate 关键字段缺失率可控
partition completeness 该分区完整

执行:

# 可执行:已与项目 runbook 对齐
docker compose --profile tools --env-file infra/env/.env.local -f infra/docker-compose.yml run --rm devbox \
  pytest tests/integration/test_week06_asset_checks.py -q

验收点:5 类 checks 都有结果;warning / skipped 必须写 reason code。

8. 生成 run evidence

reports/week06/run_evidence/*.json 至少包含:

  • run_id
  • asset_key
  • partition_key
  • status
  • started_at
  • finished_at
  • report_path
  • reason_codes

optional 字段没有启用时写 nullskipped,不要 fake passed。

执行 schema 与 evidence 生成验证:

# 可执行:已与项目 runbook 对齐
docker compose --profile tools --env-file infra/env/.env.local -f infra/docker-compose.yml run --rm devbox \
  pytest tests/contract/test_week06_run_evidence_schema.py tests/integration/test_week06_run_evidence_generation.py -q

常见错误:

  • reason_codes 写成字符串而不是数组。
  • report_path 指向不存在文件。
  • optional Week04 / Week05 依赖未启用却写成 passed

9. 照 runbook 完成交接记录

reports/week06/week06_lab_execution_summary.md 中回答:

  • 本次选择哪个 partition
  • 为什么需要 backfill / replay
  • 哪些 checks 通过或失败
  • evidence report 路径是什么
  • 下游是否可以 unblock
  • 哪些能力仍是 optional / placeholder
Important本实验不是为了截图

截图只能说明你打开过页面。实验交付要能说明:选了哪个 partition、为什么补、怎么验、证据在哪里、下游是否能继续消费。

验收清单

常见错误与排查

问题 排查
Dagster 容器 import 失败 检查 compose 挂载、runtime 依赖、模块路径
devbox 能跑但 Dagster 不能跑 说明两个 runtime 不一致,先修 runtime
async ingest 没 await 使用安全 async bridge,不复制业务 SQL
asset key 冲突 检查是否使用 week06/* namespace
evidence schema 校验失败 区分 required / optional,不要把未启用字段强制必填
partition start date 不匹配 检查 WEEK06_PARTITION_START_DATEWEEK06_DEFAULT_PARTITION
optional dependency skipped 这是可接受状态,但必须写 reason code 和 downstream decision