Implement a Time-Aware Task Management System
AirbnbSoftware Engineer · Online AssessmentLast updated
Design and implement an in-memory, time-aware task management class that supports scheduling, rescheduling, canceling, and prioritized polling based on timestamps and priorities.
Problem Statement
Many services at Airbnb require time-sensitive asynchronous actions, such as sending checkout reminder messages to guests or releasing expired reservation holds.
Implement an in-memory class TaskScheduler that manages tasks with scheduled execution times and priorities.
Class Interface
class TaskScheduler:
def __init__(self):
pass
def schedule(self, task_id: str, priority: int, execute_at: int) -> bool:
"""Schedules a new task."""
pass
def reschedule(self, task_id: str, new_execute_at: int) -> bool:
"""Updates the execution timestamp of an existing pending task."""
pass
def cancel(self, task_id: str) -> bool:
"""Cancels an existing pending task."""
pass
def poll(self, current_time: int, max_tasks: int) -> list[str]:
"""Fetches and marks as executed up to max_tasks that are due."""
pass
Behavior & Invariants
-
schedule(task_id, priority, execute_at):- Schedules a task. Returns
Trueif successfully scheduled. - If a pending task with
task_idalready exists, returnsFalsewithout modifying it.
- Schedules a task. Returns
-
reschedule(task_id, new_execute_at):- Updates
execute_atof an existing pending task, keeping its original priority. - Returns
Trueon success, orFalseiftask_iddoes not exist or has already been executed.
- Updates
-
cancel(task_id):- Removes the task from pending tasks. Returns
Trueif successfully cancelled, orFalseif it does not exist or was already executed.
- Removes the task from pending tasks. Returns
-
poll(current_time, max_tasks):- Returns up to
max_taskstask IDs whoseexecute_at <= current_time. - Ordering: Higher
priorityfirst. For tasks with equal priority, earlierexecute_atfirst. If both tie, lexicographically smallertask_idfirst. - Polled tasks are transitioned to executed and can no longer be cancelled, rescheduled, or re-polled.
- Returns up to
Constraints
- Number of operations: up to
10^5total calls across all methods. 1 = 1
Example
scheduler = TaskScheduler()
scheduler.schedule("taskA", priority=1, execute_at=100) # True
scheduler.schedule("taskB", priority=2, execute_at=120) # True
scheduler.schedule("taskC", priority=2, execute_at=90) # True
scheduler.schedule("taskA", priority=5, execute_at=50) # False (taskA already exists)
scheduler.reschedule("taskA", new_execute_at=80) # True
scheduler.cancel("taskC") # True
# At time 100, pending tasks are:
# - taskA: priority=1, execute_at=80 (due)
# - taskB: priority=2, execute_at=120 (not due yet)
scheduler.poll(current_time=100, max_tasks=5) # ["taskA"]
# At time 150:
# - taskB: priority=2, execute_at=120 (due)
scheduler.poll(current_time=150, max_tasks=1) # ["taskB"]
scheduler.cancel("taskB") # False (already executed)
Edge Cases
- Polling when no tasks are due or when queue is empty (return empty list).
max_tasksis larger than the number of available eligible tasks.- Tasks rescheduled to a later or earlier time that alters their readiness at
current_time.
Hints
Hint 1
Track active task metadata using a hash map and use lazy deletion or a versioning token to handle cancel and reschedule operations cleanly.
Hint 2
A single priority queue ordered by execution time helps filter due tasks, but ordering due tasks by priority requires separating 'due' discovery from 'execution' ordering.
Hint 3
Consider maintaining tasks in two structures: a time-sorted min-heap for unready tasks, and a priority-sorted heap for tasks that have become ready, or bucket/re-heapify ready tasks on poll.
Follow-up questions
- How would you scale this design if the number of tasks exceeded single-host memory?
- How would you guarantee at-least-once or exactly-once execution if worker nodes crash after polling tasks?
- If poll is called frequently with very small time increments, how can you avoid scanning unready tasks repeatedly?
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.