In-Memory Key-Value Database with Nested Transactions
AnthropicSoftware Engineer · OnsiteLast updated
Design an in-memory key-value store supporting nested transactions with begin, commit, rollback, get, set, and delete operations.
Problem Description
Design an in-memory key-value database that supports nested transactions. Changes made inside an active transaction should remain isolated until committed, and rolling back a transaction should restore the state prior to that transaction's initiation.
Transactions can be nested arbitrarily deep:
- When a nested transaction commits, its modifications are merged into the enclosing (parent) transaction scope.
- When the outermost transaction commits, its modifications are permanently applied to the base database.
- When any transaction is rolled back, all changes made within that transaction's scope are discarded, returning the database to the state of the parent scope.
Class Interface
Implement the TransactionalKV class:
class TransactionalKV:
def __init__(self):
pass
def set(self, key: str, value: str) -> None:
"""Sets the key to value in the current context."""
pass
def get(self, key: str) -> Optional[str]:
"""Returns the value associated with key, or None if it does not exist."""
pass
def delete(self, key: str) -> bool:
"""Deletes the key in the current context. Returns True if key existed, False otherwise."""
pass
def begin(self) -> None:
"""Starts a new transaction scope."""
pass
def commit(self) -> bool:
"""Commits the current transaction. Returns False if no transaction is active, True otherwise."""
pass
def rollback(self) -> bool:
"""Rolls back the current transaction. Returns False if no transaction is active, True otherwise."""
pass
Examples
Example 1
db = TransactionalKV()
db.set("a", "10")
db.begin()
db.set("a", "20")
db.get("a") # returns "20"
db.rollback() # returns True
db.get("a") # returns "10"
Example 2: Nested Transactions
db = TransactionalKV()
db.set("a", "1")
db.begin() # Level 1
db.set("a", "2")
db.begin() # Level 2
db.set("b", "3")
db.delete("a")
db.get("a") # returns None
db.rollback() # Rolls back Level 2
db.get("a") # returns "2"
db.get("b") # returns None
db.commit() # Commits Level 1 to base
db.get("a") # returns "2"
Constraints & Edge Cases
- Keys and values are non-empty strings.
- Calling
commit()orrollback()with no active transaction should returnFalsewithout raising an error. - A key deleted inside an inner transaction must not fall back to revealing a value from an outer scope.
- At most
10^5operations will be called.
Hints
Hint 1
Use a stack of maps/dictionaries to represent transaction layers, where each layer only records modifications (deltas) made in that specific scope.
Hint 2
When a key is deleted within a transaction, use a tombstone or sentinel value so that lookups do not mistakenly fall back to an enclosing transaction's value.
Hint 3
On commit, merge the topmost layer's deltas into the layer directly below it (or into the root base store if only one transaction level exists).
Follow-up questions
- How would you optimize `get` so that deep transaction nesting doesn't result in an O(depth) lookup time?
- How would you extend this design to support concurrent transactions using Multiversion Concurrency Control (MVCC) or optimistic locking?
- If transaction rollbacks are rare, would a write-ahead log (undo log) approach be more efficient than maintaining separate delta dictionaries?
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.