Task Management System
Reported by candidates from Airbnb's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
Airbnb reported this Task Management System OA in September 2026, and it looks scarier than it is. Strip the opcode soup and it's a simulation with one real engine: expire tasks whose expiresAt has passed before every operation. The hinted pattern is a queue, and that's the piece that keeps 200000 operations from timing out. You're building a hash map of users, a hash map of tasks, and a min-ordered expiry structure. If you've got an invite in your inbox, read the rules once, then code the expiry step first. Everything else is bookkeeping around it.
The problem
Implement a task-management service by processing a finite sequence of timestamped operations in order. Every operation begins with an opcode. Timestamps are strictly increasing. ["ADD_USER", t, userId, quota] creates a user with a maximum number of active tasks. ["CREATE", t, taskId, userId, priority, dueAt, expiresAt] creates an active task. It fails if the task identifier already exists, the user does not exist, or the user's active-task quota is full. ["GET", t, taskId] returns [taskId, userId, priority, createdAt, dueAt, expiresAt, status], or an empty row for a missing or deleted task. ["UPDATE", t, taskId, priority, dueAt, expiresAt] updates an active task. ["DELETE", t, taskId] marks an existing non-deleted task as deleted. ["SEARCH", t, userId] returns that user's active task identifiers ordered by decreasing priority, then increasing creation timestamp, then identifier. ["SET_QUOTA", t, userId, quota] changes a user's quota. Lowering it does not remove existing tasks, but new tasks remain blocked until the active count is below the quota. ["COMPLETE", t, taskId] marks an active task completed. ["HISTORY", t, userId, view] returns identifiers in increasing creation order. view is COMPLETED, EXPIRED, UNFINISHED, or OVERDUE. Unfinished tasks are active; overdue tasks are active tasks with dueAt < t. Before each operation at time t, every active task with expiresAt <= t becomes expired. Completion, deletion, and expiration free an active-task quota slot. Return one row per input operation. State-changing operations return ["OK"] on success. Failures return one of ["UNKNOWN_USER"], ["DUPLICATE_TASK"], ["QUOTA_EXCEEDED"], ["NOT_FOUND"], or ["NOT_ACTIVE"]. Search and history operations may return an empty row. Function processTaskOperations(operations: String[][]) → String[][] Examples Example 1 operations = [["ADD_USER","1","u1","2"],["CREATE","2","t1","u1","5","10","20"],["CREATE","3","t2","u1","7","4","30"],["SEARCH","5","u1"],["COMPLETE","6","t2"],["HISTORY","7","u1","COMPLETED"],["SET_QUOTA","8","u1","1"],["CREATE","9","t3","u1","9","12","40"],["GET","20","t1"]] return = [["OK"],["OK"],["OK"],["t2","t1"],["OK"],["t2"],["OK"],["QUOTA_EXCEEDED"],["t1","u1","5","2","10","20","EXPIRED"]] The higher-priority task is returned first. Completing it frees a quota slot, but the later quota reduction leaves one active task, so the next creation is rejected. The final read first expires t1. Constraints 1 <= operations.length <= 200000 All timestamps and task time fields are integers in [0, 10^9]; operation timestamps are strictly increasing. For every CREATE or UPDATE, operation timestamp < dueAt < expiresAt. User and task identifiers are nonempty ASCII strings of at most 40 characters. Priority is an integer in [-10^9, 10^9]; quota is in [0, 200000]. ADD_USER uses a new user identifier. SET_QUOTA, SEARCH, and HISTORY name an existing user. The total number of task records examined by all SEARCH and HISTORY operations is at most 200000.
Reported by candidates. Source: FastPrep
Pattern and pitfall
The trick is lazy expiration. Keep a min-heap keyed by expiresAt. Before each operation at time t, pop while top.expiresAt <= t and the task is still active and matches its current expiresAt. Mark it EXPIRED and decrement that user's active count. The pitfall is UPDATE. It changes expiresAt, so push a new heap entry and ignore stale ones on pop by checking the task's current value. Second pitfall: SET_QUOTA never evicts, so CREATE must check activeCount >= quota, not equality. SEARCH and HISTORY are bounded by the constraint, so you can scan a user's task list and sort. Keep per-user lists in creation order. Overdue means active and dueAt < t, strictly. If you blank on the live OA, StealthCoder is the silent hedge. It reads the spec and hands you the structure while you type.
The honest play: practice the pattern, and have StealthCoder ready for the one you didn't see coming.
You can drill Task Management System cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built for the candidate who saw this exact problem leak two days before his OA and wondered if anyone had a play.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass Airbnb's OA.
Airbnb reuses patterns across OAs. Built for the candidate who saw this exact problem leak two days before his OA and wondered if anyone had a play. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Task Management System FAQ
How hard is the Airbnb Task Management System OA really?+
Medium on algorithms, heavy on details. No clever graph or DP is needed. The difficulty is tracking state correctly across nine opcodes and getting expiry, quota, and update interactions right. Most failures come from missed edge cases, not wrong big-picture ideas.
What's the core trick?+
Process expiry lazily with a min-heap on expiresAt. Before every operation at time t, pop everything with expiresAt <= t and flip active tasks to EXPIRED, freeing quota. That gives O(log n) per expiry and keeps 200000 operations comfortably fast.
How do I handle UPDATE with the expiry heap?+
Don't try to delete from the heap. Push a new entry with the new expiresAt and, when popping, verify the task is still active and its current expiresAt matches the popped value. Stale entries get skipped. This lazy deletion is the standard approach.
Which edge cases break most solutions?+
Lowering a quota below the active count, since it should block new tasks without removing old ones. Also expiring before the operation runs, so a GET at the expiry time shows EXPIRED. And OVERDUE uses dueAt < t strictly, only for active tasks.
How do I prepare in 48 hours?+
Write this simulation from scratch once. Build the user and task maps, the heap expiry step, and a status-based history filter. Test with the sample, especially the final GET that triggers expiration. Practice clean state updates on every transition: complete, delete, expire.