Extend a Key-Value Store with Commits and Explicit Rollover Semantics
Otter.AiSoftware Engineer · Technical ScreenLast updated
Design a versioned key-value store supporting staging, atomic commits, point-in-time snapshot reads, and automated rollover compaction.
Scenario
Collaborative editing and transcription platforms often require client-facing storage where users can stage edits, create point-in-time checkpoints ("commits"), inspect previous revisions, and roll back if necessary. You are tasked with designing an in-memory, versioned key-value store service with explicit commit milestones and a defined rollover policy to prevent unbounded memory growth.
Requirements
Functional Requirements
- Staging Operations: Support
put(key, value)anddelete(key)within an active, uncommitted working buffer. - Commit: Support
commit() -> commit_idto atomically persist staged changes as an immutable snapshot with a monotonically increasing integer identifier. Discard the active staging buffer upon commit. - Snapshot Read: Support
get(key, commit_id=None)to read the value of a key at a specificcommit_id(defaulting to the latest committed version). If the key was deleted or never existed at that commit, return null/not found. - Rollover / Compaction: Maintain at most
Khistorical commits (e.g.,K = 50). When commitNcauses the history to exceedK, the oldest commits must roll over: collapse them into a base snapshot, freeing obsolete intermediate versions and tombstones.
Non-Functional Requirements
- Snapshot Isolation: Readers querying a specific
commit_idmust see a consistent snapshot unaffected by concurrent staging or new commits. - Memory Efficiency: Avoid duplicating entire datasets across commits; use structural sharing or delta-based versioning.
- Low Latency: Staging operations and reads should complete in
O(1)orO(log N)time.
Scale Assumptions
- Average workspace: 50,000 active keys with value sizes around 1 KB.
- Retention limit:
K = 50historical commits per workspace. - Throughput: Up to 5,000 operations/sec per instance.
What Your Answer Should Cover
- Data Model & In-Memory Structures: How keys, values, versions, and tombstones are stored to facilitate fast reads and structural sharing.
- Read & Write Lifecycle: Step-by-step mechanics for staging, committing, and point-in-time querying.
- Rollover & Garbage Collection: Detailed strategy for compacting commits beyond the
Klimit without blocking active readers. - Edge Cases & Trade-offs: Trade-offs between persistent tree structures (e.g., Path Copying / Copy-on-Write HAMT) versus per-key version lists (MVCC).
Hints
Hint 1
Compare storing an append-only version list per key (similar to MVCC) versus a copy-on-write tree structure representing the whole key space at each commit.
Hint 2
Ensure tombstones are explicitly recorded with the commit version so historical reads can distinguish between a deleted key and a missing key.
Hint 3
During rollover, collapsing multiple versions of a key only requires retaining the latest state of that key within the rolled-over range.
Follow-up questions
- How would you extend this design to support branching or multiple concurrent staging sessions (like Git branches)?
- How does your rollover mechanism ensure thread safety if a long-running read request is reading from a commit currently being compacted?
- If persistence across crashes is required, how would you incorporate write-ahead logging (WAL) and checkpointing?
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.