Implement timestamped task management system APIs
Company: Instacart
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Take-home Project
You are implementing an in-memory **Task Management System**. All API methods are called with a `timestamp` representing the logical time when the operation happens.
Assume:
- API calls are processed in **non-decreasing `timestamp` order**.
- Task IDs and user IDs are strings (or ints).
- If an operation references a non-existent task/user, it should be a no-op (or return null/false) — choose a consistent behavior and document it.
## Data model
Each task has:
- `task_id`
- `description`
- `priority` (integer), default `0` at creation
- optional assignment to one or more users (define whether multiple users can be assigned; if unspecified, assume **a task can be assigned to multiple users** and each user can have multiple tasks)
## Required APIs
### 1) CRUD tasks
Implement four methods (all take `timestamp`):
- `createTask(timestamp, task_id, description)`
- `getTask(timestamp, task_id) -> description | null`
- `updateTask(timestamp, task_id, new_description)`
- `deleteTask(timestamp, task_id)`
Deleting a task removes it from the system and unassigns it from all users.
### 2) Priorities and sorted retrieval
Implement:
- `updateTaskPriority(timestamp, task_id, new_priority)`
- `getSortedPrioritizedTasks(timestamp) -> List[task_id]`
`getSortedPrioritizedTasks` should return all **currently existing** tasks sorted by:
1. higher `priority` first
2. tie-break by `task_id` ascending (or another deterministic rule you state)
### 3) Users, assignment, and scheduled deletion
Implement:
- `addUser(timestamp, user_id)`
- `assignTaskToUser(timestamp, user_id, task_id)`
- `unassignTaskToUser(timestamp, user_id, task_id)`
- `scheduleDeletion(timestamp, task_id, delay)`
`scheduleDeletion` schedules the task to be deleted at time `timestamp + delay`.
**Important ordering rule:** If one or more deletions are scheduled to occur at time `T`, then at timestamp `T` those deletions must be applied **before any other operations at the same timestamp `T`**.
### 4) Time-travel query
Implement:
- `getUserTaskNumsAt(timestamp, user_id, time_at) -> int`
This returns the number of tasks assigned to `user_id` **at logical time `time_at`** (where `time_at <= timestamp`). The result must reflect all creates/deletes/assigns/unassigns/scheduled-deletes that happened up to `time_at`, with the same deletion-precedence rule at identical timestamps.
## Constraints (you may assume)
- Up to ~200k operations.
- Timestamps fit in 64-bit integers.
- Your implementation should be efficient (roughly near O(log n) per operation for the expensive parts).
Quick Answer: This question evaluates a candidate's ability to design and implement time-ordered in-memory data structures and APIs, testing competencies in temporal event ordering and precedence, scheduled deletions, priority-sorted retrieval, user-task assignment, and historical (time-travel) queries.
Implement an in-memory **Task Management System** driven by a list of timestamped operations. Implement `solution(operations)`: each item in `operations` is a list whose first element is the method name and whose second element is the integer `timestamp`. Operations arrive in **non-decreasing timestamp order**. Return the list of outputs produced by the query methods, in order.
**Methods** (arguments shown after `timestamp`):
- `["createTask", ts, task_id, description]` — create a task (priority defaults to 0).
- `["getTask", ts, task_id]` — append the task's `description`, or `None` if it does not exist.
- `["updateTask", ts, task_id, new_description]` — update an existing task's description.
- `["deleteTask", ts, task_id]` — delete the task and unassign it from all users.
- `["updateTaskPriority", ts, task_id, new_priority]` — set a task's priority.
- `["getSortedPrioritizedTasks", ts]` — append the list of all currently existing `task_id`s sorted by higher priority first, breaking ties by `task_id` ascending.
- `["addUser", ts, user_id]` — register a user.
- `["assignTaskToUser", ts, user_id, task_id]` — assign a task to a user (a task may be assigned to multiple users; a user may have multiple tasks).
- `["unassignTaskToUser", ts, user_id, task_id]` — remove an assignment.
- `["scheduleDeletion", ts, task_id, delay]` — schedule the task to be deleted at time `ts + delay`.
- `["getUserTaskNumsAt", ts, user_id, time_at]` — append the number of tasks assigned to `user_id` at logical time `time_at` (`time_at <= ts`).
**Deletion-precedence rule:** if one or more deletions are scheduled to fire at time `T`, those deletions must be applied **before any other operation at the same timestamp `T`** (and this same precedence is reflected in `getUserTaskNumsAt` time-travel results).
Referencing a non-existent task or user is a no-op (queries return `None`/`0`). Re-creating an id that was previously deleted is allowed.
Constraints
- Up to ~200,000 operations.
- Timestamps fit in 64-bit integers and arrive in non-decreasing order.
- Task IDs and user IDs are strings or integers.
- time_at <= timestamp for getUserTaskNumsAt.
- Expensive operations should be near O(log n).
Examples
Input: ([['createTask', 1, 't1', 'desc1'], ['getTask', 2, 't1'], ['updateTask', 3, 't1', 'desc1-updated'], ['getTask', 4, 't1'], ['getTask', 5, 'tX'], ['deleteTask', 6, 't1'], ['getTask', 7, 't1']],)
Expected Output: ['desc1', 'desc1-updated', None, None]
Explanation: Basic CRUD: create then getTask returns 'desc1'; after updateTask getTask returns the new description; getTask on a missing id 'tX' returns None; after deleteTask, getTask returns None.
Input: ([['createTask', 1, 'a', 'A'], ['createTask', 1, 'b', 'B'], ['createTask', 1, 'c', 'C'], ['updateTaskPriority', 2, 'a', 5], ['updateTaskPriority', 2, 'b', 10], ['updateTaskPriority', 2, 'c', 5], ['getSortedPrioritizedTasks', 3], ['deleteTask', 4, 'b'], ['getSortedPrioritizedTasks', 5]],)
Expected Output: [['b', 'a', 'c'], ['a', 'c']]
Explanation: Sorted retrieval orders by higher priority first, then task_id ascending: b(10) before a(5) and c(5), with a before c on the id tie-break. After deleting b, only a and c remain.
Hints
- Because operations arrive in non-decreasing timestamp order, you can flush all scheduled deletions whose due time is <= the current operation's timestamp BEFORE processing that operation. This enforces the deletion-precedence rule for free.
- Use a min-heap keyed by (due_time, sequence) for scheduled deletions, and mark each scheduled entry alive/cancelled so a manually-deleted task does not double-delete.
- For getUserTaskNumsAt, keep a per-user history of (timestamp, cumulative_count). On each assign/unassign/delete append/overwrite the entry for that timestamp, then answer a query with a binary search for the last entry whose timestamp <= time_at.
- deleteTask must also unassign the task from every user; keep a reverse index task_id -> set(user_id) so you can update each affected user's count.