Add Nested Begin, Commit, and Abort Semantics to a Shopping Cart
DecagonSoftware Engineer · Technical ScreenLast updated
Design and implement an in-memory shopping cart that supports nested transactions via begin, commit, and abort operations with proper scope isolation.
Problem Statement
Design an in-memory transactional shopping cart class, TransactionalCart, that supports nested transactions. Nested transactions allow operations to be staged, committed to parent scopes, or rolled back cleanly without affecting outer scopes or the base cart state.
Requirements
Implement the following methods in TransactionalCart:
add_item(item_id: str, quantity: int) -> None: Addsquantityunits ofitem_id.quantitywill always be a positive integer.remove_item(item_id: str, quantity: int) -> None: Removesquantityunits ofitem_id. Ifquantityis greater than or equal to the current quantity ofitem_id, the item is removed entirely. Ifitem_iddoes not exist in the cart, raise aKeyError.get_quantity(item_id: str) -> int: Returns the current quantity ofitem_idvisible in the current transaction scope. Returns0if the item is not present.begin() -> None: Starts a new nested transaction scope.commit() -> None: Merges the changes from the current nested transaction into the parent transaction scope (or base state if it was the outermost transaction). Raises anInvalidTransactionErrorif no transaction is active.abort() -> None: Discards all modifications made in the current transaction scope, restoring state to the moment the correspondingbegin()was called. Raises anInvalidTransactionErrorif no transaction is active.
All cart modifications must immediately be visible to subsequent queries in the current scope and any child scopes opened afterwards.
Example
cart = TransactionalCart()
cart.add_item("apple", 3)
assert cart.get_quantity("apple") == 3
cart.begin() # Depth 1
cart.add_item("apple", 2)
cart.add_item("banana", 5)
assert cart.get_quantity("apple") == 5
cart.begin() # Depth 2
cart.remove_item("apple", 4)
assert cart.get_quantity("apple") == 1
cart.abort() # Discards Depth 2 changes
assert cart.get_quantity("apple") == 5
assert cart.get_quantity("banana") == 5
cart.commit() # Commits Depth 1 changes to base state
assert cart.get_quantity("apple") == 5
assert cart.get_quantity("banana") == 5
Constraints
- Nested transaction depth can reach up to
1,000levels. - Total operations (reads, writes, transaction controls) will be up to
100,000. item_idis an alphanumeric string of length 1 to 32 characters.quantityis an integer where1 <= quantity <= 10^6.
Hints
Hint 1
Consider whether a full snapshot copy at each `begin()` or a stack of diffs/undo actions scales better when transactions modify only a small subset of items.
Hint 2
If using a stack of delta maps, committing an inner transaction means merging its diffs into the parent level's diff rather than directly updating the root state.
Hint 3
Ensure operations like `remove_item` that completely eliminate an item record explicit 'tombstones' or absolute values in your diff layer so ancestor values are not inadvertently exposed.
Follow-up questions
- How would you adapt your design if calculating the cart total requires dynamic discount evaluation that depends on cumulative basket value?
- How would you handle thread-safety if multiple background tasks or client sessions share the same transaction tree?
- How would you implement an append-only write-ahead log (WAL) to make these nested transactions crash-resilient without degrading read latency?
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.