PracHub
QuestionsCoachesLearningGuidesInterview Prep

Quick Overview

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.

  • medium
  • Otter.Ai
  • Coding & Algorithms
  • Software Engineer

In-Memory Key-Value Store with Nested Transactions

Company: Otter.Ai

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

# In-Memory Key-Value Store with Nested Transactions Implement an in-memory key-value store that supports basic reads and writes **and** transactions that can be committed or rolled back, including **nested** transactions. Both keys and values are strings. The store starts empty. ## Operations to implement Implement a class (suggested name `KeyValueStore`) exposing the following methods. ### Basic operations - `set(key, value)` — Associate `key` with `value`. If `key` already has a value, overwrite it. Returns nothing. - `get(key)` — Return the current value associated with `key`, taking any open transactions into account. If `key` does not currently have a value, return `null` (use your language's null/None equivalent). - `delete(key)` — Remove `key` and its value from the store. After a delete, `get(key)` returns `null` until the key is set again. Deleting a key that does not exist is a no-op. Returns nothing. ### Transaction operations - `begin()` — Open a new transaction. Transactions may be **nested**: calling `begin()` again while one or more transactions are already open opens an additional, inner transaction. While at least one transaction is open, every `set` and `delete` is buffered in the **innermost** open transaction and is not yet visible to any future `commit` of an outer scope until this transaction is committed. Returns nothing. - `commit()` — Commit the **innermost** open transaction. Its buffered writes and deletes are merged into the enclosing scope: the parent transaction if one exists, otherwise the base (committed) store. Return `true` if a transaction was open and committed; return `false` if there was no open transaction (in which case the store is left unchanged). - `rollback()` — Discard the **innermost** open transaction, throwing away all of its buffered writes and deletes so they never take effect. Return `true` if a transaction was open and rolled back; return `false` if there was no open transaction (in which case the store is left unchanged). ## Read semantics `get(key)` must reflect the combined view of the base store plus all currently open transactions, where inner transactions take precedence over outer ones: 1. Search the open transactions from innermost to outermost. If one of them records a write or a delete for `key`, return that record's value (`null` for a recorded delete). 2. If no open transaction records `key`, return the value from the base (committed) store, or `null` if it has none. A `set` inside a transaction shadows any value the key had in an outer scope, and a `delete` inside a transaction shadows it with "absent" (so `get` returns `null`) until that transaction is committed or rolled back. ## Examples The following call sequence shows the expected return values. Calls that return nothing are shown without a result. ``` set("a", "1") get("a") -> "1" begin() # open transaction T1 set("a", "2") get("a") -> "2" # T1's write shadows the base value begin() # open nested transaction T2 delete("a") get("a") -> null # T2's delete shadows T1's value rollback() -> true # discard T2 (the delete is thrown away) get("a") -> "2" # T1's value is visible again commit() -> true # merge T1 into the base store get("a") -> "2" # change is now permanent rollback() -> false # no transaction is open get("a") -> "2" # unchanged ``` Another example showing that a rolled-back outer transaction discards everything it contained: ``` set("x", "10") begin() set("x", "20") set("y", "30") get("x") -> "20" get("y") -> "30" rollback() -> true get("x") -> "10" # the buffered "20" is gone get("y") -> null # "y" was only ever set inside the rolled-back txn ``` ## Constraints & Assumptions - Keys and values are non-empty strings. You may assume at most $10^5$ total operations across a single run. - Transactions can be nested to a depth of at least several hundred; your solution should not assume a fixed small depth. - `commit` and `rollback` always act on the single innermost open transaction. There is no operation that commits or rolls back all open transactions at once. - All operations should run in time proportional to the work they touch; in particular `get`, `set`, and `delete` should be efficient (roughly $O(d)$ in the number of currently open transactions $d$, or better), and `commit`/`rollback` should touch only the keys buffered in the innermost transaction. - The store is single-threaded; you do not need to handle concurrent access.

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.

# In-Memory Key-Value Store with Nested Transactions Implement an in-memory key-value store that supports basic reads and writes **and** transactions that can be committed or rolled back, including **nested** transactions. Both keys and values are strings. The store starts empty. ## Operations to implement Implement a class (suggested name `KeyValueStore`) exposing the following methods. ### Basic operations - `set(key, value)` — Associate `key` with `value`. If `key` already has a value, overwrite it. Returns nothing. - `get(key)` — Return the current value associated with `key`, taking any open transactions into account. If `key` does not currently have a value, return `null` (use your language's null/None equivalent). - `delete(key)` — Remove `key` and its value from the store. After a delete, `get(key)` returns `null` until the key is set again. Deleting a key that does not exist is a no-op. Returns nothing. ### Transaction operations - `begin()` — Open a new transaction. Transactions may be **nested**: calling `begin()` again while one or more transactions are already open opens an additional, inner transaction. While at least one transaction is open, every `set` and `delete` is buffered in the **innermost** open transaction and is not yet visible to any future `commit` of an outer scope until this transaction is committed. Returns nothing. - `commit()` — Commit the **innermost** open transaction. Its buffered writes and deletes are merged into the enclosing scope: the parent transaction if one exists, otherwise the base (committed) store. Return `true` if a transaction was open and committed; return `false` if there was no open transaction (in which case the store is left unchanged). - `rollback()` — Discard the **innermost** open transaction, throwing away all of its buffered writes and deletes so they never take effect. Return `true` if a transaction was open and rolled back; return `false` if there was no open transaction (in which case the store is left unchanged). ## Read semantics `get(key)` must reflect the combined view of the base store plus all currently open transactions, where inner transactions take precedence over outer ones: 1. Search the open transactions from innermost to outermost. If one of them records a write or a delete for `key`, return that record's value (`null` for a recorded delete). 2. If no open transaction records `key`, return the value from the base (committed) store, or `null` if it has none. A `set` inside a transaction shadows any value the key had in an outer scope, and a `delete` inside a transaction shadows it with "absent" (so `get` returns `null`) until that transaction is committed or rolled back. ## Examples The following call sequence shows the expected return values. Calls that return nothing are shown without a result. ``` set("a", "1") get("a") -> "1" begin() # open transaction T1 set("a", "2") get("a") -> "2" # T1's write shadows the base value begin() # open nested transaction T2 delete("a") get("a") -> null # T2's delete shadows T1's value rollback() -> true # discard T2 (the delete is thrown away) get("a") -> "2" # T1's value is visible again commit() -> true # merge T1 into the base store get("a") -> "2" # change is now permanent rollback() -> false # no transaction is open get("a") -> "2" # unchanged ``` Another example showing that a rolled-back outer transaction discards everything it contained: ``` set("x", "10") begin() set("x", "20") set("y", "30") get("x") -> "20" get("y") -> "30" rollback() -> true get("x") -> "10" # the buffered "20" is gone get("y") -> null # "y" was only ever set inside the rolled-back txn ``` ## Constraints & Assumptions - Keys and values are non-empty strings. You may assume at most $10^5$ total operations across a single run. - Transactions can be nested to a depth of at least several hundred; your solution should not assume a fixed small depth. - `commit` and `rollback` always act on the single innermost open transaction. There is no operation that commits or rolls back all open transactions at once. - All operations should run in time proportional to the work they touch; in particular `get`, `set`, and `delete` should be efficient (roughly $O(d)$ in the number of currently open transactions $d$, or better), and `commit`/`rollback` should touch only the keys buffered in the innermost transaction. - The store is single-threaded; you do not need to handle concurrent access.

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

  1. 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.
  2. 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.
  3. 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.
  4. 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).
  5. rollback simply discards (pops) the innermost layer; both commit and rollback return false when the stack is empty.
Last updated: Jul 1, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Evaluate Arithmetic Expressions - Otter.Ai (medium)
  • Implement an Arithmetic Expression Evaluator - Otter.Ai (medium)
  • Fixed-Width Text Display - Otter.Ai (medium)
  • Validate Password Against Multiple Rules - Otter.Ai (easy)