Durable Session Cache: Byte Checkpoints, Segments, Thread Safety
OpenAISoftware Engineer · Technical ScreenLast updated
A three-part exercise building a durable in-memory session cache: serialize entries to length-prefixed bytes and checkpoint them to disk, split the checkpoint into size-capped segments that reassemble by concatenation, then discuss making the class thread-safe. Tests byte-level encoding, exact round-tripping, and concurrency reasoning.
Implement an in-memory session cache that can survive a process restart. While running, it serves fetch/store entirely from memory. Calling checkpoint() writes every entry to disk; constructing a new instance against the same path restores exactly what was last checkpointed.
class SessionCache:
def __init__(self, path: str) -> None: ... # loads a prior checkpoint from path, if any
def fetch(self, key: str) -> str | None: ...
def store(self, key: str, value: str) -> None: ...
def checkpoint(self) -> None: ... # persists everything to disk
Keys and values are strings that may contain any Unicode text, including newlines and emoji. Nothing needs to hit disk between checkpoint() calls.
Part 1 — Byte-level checkpoint format
On checkpoint(), serialize every key, every value, and enough length information to recover them unambiguously, into a single byte string written to path. On restore, parse that byte string back into the exact same key/value pairs.
What this part should cover:
- The exact byte layout of one record: how a length is encoded (width, byte order) and precisely what it counts (bytes, not characters).
- A correct round trip for an empty string value, a value containing a newline, and a completely empty cache.
- Defined behavior when the checkpoint file is missing (start empty) versus present but truncated mid-record (raise, rather than silently returning partial data).
Part 2 — Splitting a checkpoint into size-capped segments
Given a maximum segment size in bytes, write the serialized checkpoint as a sequence of segments, none exceeding that size, such that concatenating the segments back together in order reproduces the original bytes exactly (segments do not need to align with record boundaries).
def split_into_segments(data: bytes, max_size: int) -> list[bytes]: ...
def join_segments(segments: list[bytes]) -> bytes: ...
Part 3 — Discussion: thread safety
fetch, store, and checkpoint() may now be called concurrently from multiple threads. Without writing the full implementation, describe what could go wrong with the Part 1/2 code as written, and what locking (or other) strategy you'd add, including whether checkpoint() should block fetch/store for its whole duration or only part of it.
Hints
Hint 1
Pick a fixed-width length field up front (e.g. 4 bytes, big-endian) and always measure it in encoded bytes, not characters — a non-ASCII string's byte length and character length differ.
Hint 2
Writing key-length, key-bytes, value-length, value-bytes back to back for every entry, with nothing else in the file, makes the parser a simple 'read a length, read that many bytes, repeat until end of file' loop.
Hint 3
Segmentation only needs to slice the already-serialized byte string at fixed offsets — it doesn't need to know anything about where one record ends and the next begins.
Hint 4
For thread safety, separate the lock that protects the in-memory dict (short critical sections) from the question of what checkpoint() should see — snapshotting the dict under the lock, then writing to disk outside it, avoids blocking reads/writes for the whole file write.
Follow-up questions
- How would you add an atomic 'checkpoint never leaves a half-written file on disk even if the process crashes mid-write' guarantee?
- How would you support a delete operation that also needs to survive a restart, given this byte format?
- If segments are stored as separate files instead of one file, how would you detect that one is missing or arrived out of order before trusting a restore?
- How would the thread-safety approach change if checkpoint() needed to run automatically on a timer in the background, rather than only on demand?
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.