SWE · Event Onsite System Design

Microsoft ETL Job Scheduler:依赖调度、重试和可重放输出

以 DAG 定义提取、转换和加载作业,分开计划触发、运行租约、任务幂等与输出提交。

题目重点:以 DAG 定义提取、转换和加载作业,分开计划触发、运行租约、任务幂等与输出提交。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练版 Pipeline 是有向无环任务图,各任务读确定的输入分区、产生版本化输出;按计划或手动触发 run。
  • 任务可因 worker 崩溃而至少一次执行,因此副作用须按 (pipelineId,runId,taskId,partitionId) 幂等;题目未明确精确一次合同。
  • LB/cache/queue 是候选组件,是否需要取决于流量与读取模式;不把题目泛称组件当具体接口。

解题路线

  1. 控制面存 DAG、版本、调度日历、输入水位、重试策略和并发上限;发布前判环并校验数据分区与权限。
  2. 触发器创建唯一 runId,只有全部上游任务成功且输入可见的节点才入队;队列事件带任务尝试号,worker 领取租约并周期续约,超时可重新投递。
  3. 执行层先写临时/版本化输出,再以条件提交指针或 manifest 原子公布;重复尝试只能有一个版本成为本 run 的正式输出。下游只读已提交 manifest。
  4. 重试区分暂时错误与数据永久错误,指数退避、重试预算与死信/人工处理;补历史数据创建新 run/backfillId,不直接覆盖线上当前水位。
  5. 资源池按任务类型、租户和优先级隔离,控制并发和数据倾斜;缓存仅存可重复利用的元数据/只读输入,不替代任务状态数据库。
  6. 观测每个 run 的 DAG 状态、输入输出版本、延迟、重试、队列积压与 data quality;SLAs 从源到表的水位衡量,支持单任务重跑与整段回放。

手动走一遍

A 抽取完成后 B、C 可并行转换,D 加载须等 B、C 正式提交。B worker 写完文件却在提交前崩溃;重试可重复计算,但 D 不会读到未提交临时文件。若 B 已提交而确认消息丢失,重试用 run/task key 发现同一 manifest,不再创建第二份正式输出。

规模与取舍

V 个任务、E 条依赖的拓扑检查 O(V+E);实际作业时间由最慢关键路径和可用 worker 并发决定。调度元数据 O(V+E+运行次数),数据存储取决于分区量、版本保留和回填窗口;题目无具体规模。

易错点与追问

容易失分的边界

  • 把消息队列的至少一次投递误称每个 ETL 副作用都恰好一次。
  • 下游读取上游未提交的临时文件。
  • worker 失联后无租约/超时,任务永久卡住。
  • 补数直接覆盖当前产物,无法审计或回滚。
  • 只画 LB、cache、queue 方框,没有 DAG 状态与幂等键。

继续深挖

  • 依赖上游数据迟到时怎样控制水位和跳过策略?
  • 巨大单分区拖慢关键路径时如何拆分?
  • 历史回填与实时每日任务共享资源池怎样避免互相挤压?
  • 任务代码升级后重跑旧数据用哪个版本?
✓已复制微信号!