Schedule Dependent Tasks on a Bounded Thread Pool
BalyasnySoftware Engineer · OnsiteLast updated
This problem tests the ability to design a task scheduling system that manages complex dependency graphs while operating under strict concurrency constraints.
Problem Statement
Design a task scheduling system that executes jobs with inter-dependencies using a fixed-size thread pool. The system must ensure that a task only executes once all its prerequisites have successfully completed. If a prerequisite fails, the downstream tasks must be marked as 'blocked' or 'cancelled' appropriately.
Functional Requirements
- Dependency Resolution: Support a Directed Acyclic Graph (DAG) structure for task dependencies.
- Bounded Execution: Utilize a thread pool of size
N. The system must not exceed this concurrency limit. - Failure Propagation: If a task fails, all tasks transitively dependent on it must be prevented from running.
- Dynamic Submission: Support adding new tasks to the graph while the scheduler is running.
Non-Functional Requirements
- Thread Safety: Multiple producers may submit tasks concurrently.
- Efficiency: Minimize idle CPU time; maximize thread utilization without violating dependencies.
- Scalability: The system should handle thousands of tasks without excessive memory overhead.
Your Deliverables
- API Design: Define the interface for submitting tasks and defining dependencies (e.g.,
submit(task_id, callable, dependencies)). - Data Model: How will you represent the dependency graph and track the state of each task (e.g., PENDING, RUNNING, COMPLETED, FAILED)?
- Architecture: Explain the synchronization mechanism (e.g., how workers wait for dependencies) and how you detect when a task becomes eligible for execution.
- Failure Handling: Describe the mechanism for propagating failure states through the DAG.
Hints
Hint 1
Represent the dependency graph using an adjacency list and keep an atomic counter for each task representing the number of incomplete prerequisites.
Hint 2
Use a thread-safe task queue to hold tasks whose prerequisite counters have reached zero, allowing worker threads to pull from this queue.
Hint 3
When a task completes, decrement the counters of its dependent tasks and add those that reach zero to the task queue.
Follow-up questions
- How would you implement task priorities if some tasks are more time-sensitive than others?
- What changes would be necessary to support periodic tasks or tasks that need to retry upon transient failure?
- How would you handle a scenario where the dependency graph contains a cycle, and how could you detect this at submission time?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.