Design event timeout detector
Company: Applied Intuition
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's competency in designing efficient event-driven algorithms and time-based state management, including handling duplicates, out-of-order events, concurrent updates, and edge-case timeout semantics.
Constraints
- 0 <= len(operations) <= 200000
- event_id and timestamps are integers in the range [-10^9, 10^9]
- type is one of 'start', 'ping', or 'end'
- All `get` operations are given in non-decreasing `now_ts` order
Examples
Input: (5, [('process', 1, 'start', 0), ('process', 2, 'start', 1), ('process', 1, 'ping', 3), ('get', 6), ('process', 2, 'end', 7), ('get', 9)])
Expected Output: [[], [1]]
Explanation: At time 6, event 1 was updated at 3 and event 2 at 1, so neither satisfies `now - last_updated > 5`. After event 2 ends, time 9 makes event 1 timed out because `9 - 3 = 6 > 5`.
Input: (4, [('process', 10, 'start', 5), ('process', 10, 'ping', 3), ('process', 10, 'start', 5), ('process', 10, 'ping', 9), ('process', 10, 'end', 8), ('get', 13), ('get', 14)])
Expected Output: [[], [10]]
Explanation: The ping at 3 and repeated start at 5 are stale/duplicate and ignored. The end at 8 is stale because the latest accepted timestamp is 9. At time 13, `13 - 9 = 4` is not greater than 4, but at time 14 it is.
Input: (3, [('process', 1, 'end', 2), ('process', 1, 'start', 4), ('process', 1, 'start', 6), ('get', 9), ('get', 10), ('process', 1, 'start', 11), ('get', 11), ('get', 15)])
Expected Output: [[], [1], [], [1]]
Explanation: The first end is ignored because event 1 was never active. The second start at 6 restarts the event. It times out at time 10, then a newer start at 11 clears the timeout, and it times out again at 15.
Input: (-1, [('process', 2, 'start', 7), ('get', 7), ('process', 2, 'ping', 8), ('get', 8), ('process', 3, 'ping', 0), ('get', 8)])
Expected Output: [[2], [2], [2]]
Explanation: With `T = -1`, an active event satisfies the timeout rule immediately on the next `get` because `now - last_updated > -1`. The ping for event 3 is ignored because it never started.
Input: (2, [('process', 5, 'start', 0), ('process', 3, 'start', 1), ('process', 4, 'start', 0), ('get', 3), ('process', 5, 'ping', 4), ('get', 4), ('get', 7)])
Expected Output: [[4, 5], [4, 3], [4, 3, 5]]
Explanation: At time 3, events 4 and 5 time out together, and smaller `event_id` comes first. Event 5 is later refreshed by a ping, so it leaves the timed-out set. Event 3 then times out at time 4, and event 5 times out again at time 7.
Input: (0, [('process', 1, 'start', 10), ('get', 10), ('get', 11), ('process', 1, 'ping', 11), ('get', 11), ('process', 1, 'end', 11), ('get', 12)])
Expected Output: [[], [1], [], []]
Explanation: With `T = 0`, an event times out only when `now_ts` is strictly greater than its last update time. So it is not timed out at 10, is timed out at 11, the ping at 11 clears it, and the end at 11 removes it.
Hints
- Use a hash map to store the current state of each event: whether it is active, its latest accepted timestamp, and a version number.
- Use a min-heap of expiration deadlines (`last_updated_ts + T`) plus lazy deletion so old heap entries do not need to be removed immediately.