SWE(汇总帖未给具体岗位) · OOD / Technical Coding 题型汇总

Netflix 依赖任务框架:Task 接口、DAG 调度与失败边界

从面经汇总明确的 task dependency framework 方向出发,设计最小任务接口,先验证未知依赖与环,再用 Kahn 算法执行,并讨论失败/重试与生产调度。

题目重点:从面经汇总明确的 task dependency framework 方向出发,设计最小任务接口,先验证未知依赖与环,再用 Kahn 算法执行,并讨论失败/重试与生产调度。

题意与假设

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

  • 训练版 `tasks` 是 `task_id→零参 callable`,`dependencies[id]` 列先决任务;单线程、确定性插入顺序。题目未明确 API。
  • 任务成功才算完成;任意任务抛异常即终止当前运行,不继续执行其后继。已经产生的外部副作用不会自动回滚。
  • 依赖列表同一 prerequisite 重复只计一次;缺失 task 或环报 ValueError。

解题路线

  1. 先校验所有 task id 与 prerequisite id,建立每个节点 indegree 和前驱→后继表。若要求任务结果作为后继输入,Task 接口需再增加依赖结果映射;最小示例仅演示顺序执行。
  2. 把 indegree=0 的任务放 ready 队列;执行成功后逐个降低后继 indegree,降到 0 才入队。最终完成数小于任务数表明有环或未满足依赖。
  3. 生产版本将 ready 队列持久化,worker 领取任务需 lease/attempt id;区分失败可重试、永久失败、取消与跳过后继。并发执行时 indegree 变更与状态转移必须原子化;重试需幂等或结果去重。

手动走一遍

训练例 A、B 无依赖,C 依赖 A/B,D 依赖 C。ready 初始为 A/B;两者成功后 C 才可执行,随后 D。若 C 依赖 D,则 C↔D 成环,最终无法完成全部节点。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

from collections import defaultdict, deque


def run_tasks(tasks, dependencies):
    """tasks maps ID to zero-argument callable; deps maps ID to prerequisite IDs."""
    indegree = {task_id: 0 for task_id in tasks}
    children = defaultdict(list)
    for task_id, prerequisites in dependencies.items():
        if task_id not in tasks:
            raise ValueError("unknown task")
        for prerequisite in set(prerequisites):
            if prerequisite not in tasks:
                raise ValueError("unknown prerequisite")
            indegree[task_id] += 1
            children[prerequisite].append(task_id)
    ready = deque(task_id for task_id in tasks if indegree[task_id] == 0)
    order = []
    while ready:
        task_id = ready.popleft()
        order.append(task_id)
        for child in children[task_id]:
            indegree[child] -= 1
            if indegree[child] == 0:
                ready.append(child)
    if len(order) != len(tasks):
        raise ValueError("dependency cycle")
    completed = {}
    for task_id in order:
        completed[task_id] = tasks[task_id]()
    return completed

复杂度

V 个任务、E 条去重依赖,建图和 Kahn 调度 O(V+E),空间 O(V+E),另加任务本身执行时间。单线程示例 ready 顺序由输入顺序决定,不能据此声称实现了生产级并行 scheduler。

易错点与追问

容易失分的边界

  • 任务一提交就运行,尚未等待所有依赖成功。
  • 未知依赖静默忽略,导致本应阻塞的任务提前运行。
  • 重复依赖把 indegree 多算,永远无法 ready。
  • 发生环时一直等待而不报错。
  • 任务失败后直接把后继当成功 ready,或说已自动回滚外部副作用。

继续深挖

  • 多个 worker 如何并行领取 ready 任务且不重复执行?
  • 失败任务的后继是取消、跳过还是可在人工修复后继续?
  • 依赖结果如何传给后继而不把大对象留在 scheduler 内存?
  • 图运行中动态添加任务会改变何种不变量?
✓已复制微信号!