Katkat

Execute Dependency-Constrained Work at Scale

MetaSoftware Engineer · Onsite

Last updated

This problem tests the ability to design an execution engine that manages task dependencies using a directed acyclic graph (DAG), respects concurrency limits, and handles task failures.

Problem: Dependency-Aware Task Runner

You are tasked with building a task execution engine that processes a set of jobs based on their dependencies. Each job has a unique ID, a list of IDs it depends on (pre-requisites), and a function to execute. A job can only start once all its dependencies have completed successfully.

Requirements

  1. Dependency Resolution: A job must not start until all its dependencies have finished.
  2. Concurrency Limiting: You must support a max_workers parameter to limit the number of jobs executing simultaneously.
  3. Failure Propagation: If a job fails, all jobs that depend on it (directly or indirectly) must be cancelled or marked as failed, and the engine should stop scheduling them.
  4. Execution: Provide a method execute_all(jobs) that returns a dictionary mapping job IDs to their execution status (SUCCESS, FAILED, or CANCELLED).

Interface

class Job:
    def __init__(self, job_id, dependencies, task_func):
        self.job_id = job_id
        self.dependencies = dependencies  # List of job_ids
        self.task_func = task_func

def execute_all(jobs: List[Job], max_workers: int) -> Dict[str, str]:
    # Returns map of {job_id: status}
    pass

Constraints

  • jobs count: up to 10,000.
  • max_workers: up to 100.
  • The graph is guaranteed to be a Directed Acyclic Graph (DAG).

Example

Input: Jobs A (no deps), B (deps: [A]), C (deps: [A]). If A succeeds, B and C can run concurrently. If A fails, B and C are cancelled.

Edge Cases

  • Cycles in dependencies (the problem guarantees a DAG, but how would you detect/handle an invalid graph?).
  • A job depending on a non-existent ID.
  • Empty list of jobs.

Hints

Hint 1

Represent the graph as an adjacency list where each node keeps track of its 'in-degree' (number of pending dependencies).

Hint 2

Use a thread-safe queue to manage jobs that are ready to run (in-degree of 0) and a thread pool to manage the workers.

Hint 3

Use a thread-safe data structure to track the state of each job and implement a mechanism to propagate failure status to downstream dependencies.

Follow-up questions

  • How would you modify this engine to support task retries with exponential backoff?
  • If the tasks were distributed across multiple physical nodes instead of local threads, what components would you need to add to maintain consistency?
  • How would you implement a priority queue mechanism to ensure high-priority jobs get executed before others when the worker pool is saturated?