Simulate Transaction Lock Scheduling
Company: Nextdoor
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of concurrency control, lock scheduling, transaction state management, and queuing behavior in the Coding & Algorithms domain, focusing on practical application through an in-memory transaction lock simulator.
Constraints
- 0 <= len(operations) <= 2 * 10^5
- Transaction ids in `begin` operations are unique
- Every non-`begin` operation refers to an existing transaction that has not already finished
- The total number of queued operations and total length of all strings fit in memory
Examples
Input: ([('begin', 1), ('acquire', 1, 'A'), ('echo', 1, 't1 has A'), ('begin', 2), ('acquire', 2, 'A'), ('echo', 2, 't2 has A'), ('finish', 1), ('echo', 2, 't2 still running'), ('finish', 2)],)
Expected Output: ['t1 has A', 't2 has A', 't2 still running']
Explanation: Transaction 2 blocks on A. When transaction 1 finishes, A is released, transaction 2 resumes, its queued acquire succeeds, and its queued echo runs before the later external echo.
Input: ([],)
Expected Output: []
Explanation: No operations means no emitted messages.
Input: ([('begin', 1), ('acquire', 1, 'A'), ('acquire', 1, 'A'), ('echo', 1, 'still active'), ('finish', 1)],)
Expected Output: ['still active']
Explanation: Reacquiring a lock already held by the same transaction is a no-op.
Input: ([('begin', 1), ('acquire', 1, 'A'), ('begin', 2), ('acquire', 2, 'A'), ('finish', 2), ('echo', 1, 'done'), ('finish', 1)],)
Expected Output: ['done']
Explanation: Transaction 2 blocks on A, so its finish is queued behind the blocked acquire. After transaction 1 finishes, transaction 2 resumes, acquires A, and then immediately finishes without emitting anything.
Input: ([('begin', 1), ('acquire', 1, 'B'), ('acquire', 1, 'A'), ('begin', 2), ('acquire', 2, 'A'), ('echo', 2, 'got A'), ('begin', 3), ('acquire', 3, 'B'), ('echo', 3, 'got B'), ('finish', 1), ('finish', 3), ('finish', 2)],)
Expected Output: ['got B', 'got A']
Explanation: Transaction 1 releases multiple locks when it finishes. Because locks are released in acquisition order, B is released before A, so transaction 3 resumes before transaction 2.
Hints
- Keep separate state for each transaction: active/blocked/finished, its pending operation queue, the lock it is waiting on, and the locks it currently holds.
- For each lock, store its current owner and a FIFO wait queue. When a transaction finishes, wake at most one waiter per released lock, then let awakened transactions run until they block again or finish.