In-Memory Key-Value Store with Nested Transactions
Company: Otter.Ai
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's ability to design an in-memory data structure that supports layered, nested transactional state with commit and rollback semantics. It tests practical system design skills around scoping, state shadowing, and efficient lookups across multiple active contexts, a common way interviewers probe object-oriented design and data structure fundamentals in coding rounds.
Constraints
- Keys and values are non-empty strings; the store starts empty.
- At most 10^5 total operations across a single run.
- Transactions can be nested to a depth of at least several hundred; do not assume a fixed small depth.
- commit and rollback act only on the single innermost open transaction (there is no commit-all / rollback-all).
- commit and rollback return false when no transaction is open, and leave the store unchanged.
- get/set/delete should run in roughly O(d) or better; commit/rollback should touch only the innermost transaction's buffered keys.
Examples
Input: (["KeyValueStore","set","get","begin","set","get","begin","delete","get","rollback","get","commit","get","rollback","get"], [[], ["a","1"], ["a"], [], ["a","2"], ["a"], [], ["a"], ["a"], [], ["a"], [], ["a"], [], ["a"]])
Expected Output: [None, None, "1", None, None, "2", None, None, None, True, "2", True, "2", False, "2"]
Explanation: Full walkthrough from the prompt: T1 write shadows base; nested T2 delete shadows T1; rollback restores T1; commit makes it permanent; a final rollback with no open txn returns False.
Input: (["KeyValueStore","set","begin","set","set","get","get","rollback","get","get"], [[], ["x","10"], [], ["x","20"], ["y","30"], ["x"], ["y"], [], ["x"], ["y"]])
Expected Output: [None, None, None, None, None, "20", "30", True, "10", None]
Explanation: Rolling back the outer transaction throws away both buffered writes: x reverts to its base value 10 and y (only ever set in the txn) becomes null.
Input: (["KeyValueStore","begin","set","begin","set","commit","get","commit","get"], [[], [], ["a","1"], [], ["a","2"], [], ["a"], [], ["a"]])
Expected Output: [None, None, None, None, None, True, "2", True, "2"]
Explanation: Committing the innermost txn (a=2) merges it into its parent, overwriting the parent's a=1; committing the parent then merges a=2 into the base store.
Input: (["KeyValueStore","get","delete","get","commit","rollback"], [[], ["missing"], ["missing"], ["missing"], [], []])
Expected Output: [None, None, None, None, False, False]
Explanation: Boundary: reading and deleting a never-set key are safe no-ops (get returns null), and commit/rollback with no open transaction return False.
Input: (["KeyValueStore","set","begin","delete","get","commit","get","begin","set","get","rollback","get"], [[], ["k","v"], [], ["k"], ["k"], [], ["k"], [], ["k","w"], ["k"], [], ["k"]])
Expected Output: [None, None, None, None, None, True, None, None, None, "w", True, None]
Explanation: A delete buffered in a txn hides the base value (get -> null) and, once committed, removes k from the base; a later set inside a rolled-back txn is discarded, leaving k absent again.
Hints
- Represent each open transaction as its own key -> value map (a 'layer') and keep the layers on a stack, with the committed base store at the bottom.
- For get, scan the layers from innermost to outermost and return the value from the first layer that records the key; only fall back to the base store if no open layer mentions it.
- Model a delete inside a transaction as a tombstone: map the key to a null/None marker so it shadows outer values with 'absent' until the transaction is committed or rolled back.
- commit pops the innermost layer and merges its entries (writes and tombstones) into the enclosing scope: the parent layer if one exists, otherwise the base store (where a tombstone means remove the key).
- rollback simply discards (pops) the innermost layer; both commit and rollback return false when the stack is empty.