Design a Transactional Key-Value Store
Applied IntuitionSoftware Engineer · Technical ScreenLast updated
This problem tests the ability to design an in-memory key-value store that supports nested transactions, including atomic commits, rollbacks, and precise state management across layers.
Design a Transactional Key-Value Store
Design an in-memory key-value store class TransactionStore that supports standard CRUD operations alongside transaction management. The store must support nested transactions, where changes made within a sub-transaction are only visible to the parent transaction until committed.
Requirements
put(key, value): Associates a value with a key. If a transaction is active, the change must be scoped to the current transaction.get(key): Returns the value associated with the key. It must reflect the most recent state visible to the current transaction (checking the current scope, then parent scopes, then the global store).delete(key): Removes the key. If inside a transaction, the deletion must be tracked so it is effectively hidden or removed upon commit.begin(): Starts a new transaction scope. Transactions can be nested.commit(): Flushes changes from the current scope into the parent scope. If at the root level, changes are finalized.rollback(): Discards all changes made in the current transaction scope.
Constraints
- The store should handle arbitrary nesting levels.
- A
getoperation must return a special indicator (e.g.,None) if the key does not exist or has been deleted in the current scope.
Example
store = TransactionStore()
store.put("a", 1)
store.begin()
store.put("a", 2)
print(store.get("a")) # Expected: 2
store.rollback()
print(store.get("a")) # Expected: 1
Edge Cases
- Deleting a key that was added in a parent transaction.
- Committing an empty transaction.
- Calling
rollback()at the base level (should be ignored or raise an error).
Hints
Hint 1
Consider using a stack of dictionaries to represent each transaction layer, where the top of the stack is the current scope.
Hint 2
For the 'delete' operation, use a special 'tombstone' value or a separate set to distinguish between a key that doesn't exist and a key that has been explicitly deleted within the current transaction.
Hint 3
When a transaction commits, merge the top dictionary into the dictionary immediately below it in the stack.
Follow-up questions
- How would you optimize the memory usage if the store contains millions of keys but transactions only touch a small subset?
- How would this implementation change if you needed to support multi-threaded access to the store?
- If this were to be persisted to disk, how would you implement a Write-Ahead Log (WAL) to ensure durability in case of a crash?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.