Design a Shared, Crash-Safe Telemetry Buffer for Thousands of Worker Threads
DatabricksSoftware Engineer · OnsiteLast updated
Design a single, thread-shared component that lets up to 4,000 worker threads inside one process append small telemetry records to one local file, durably and without a disk sync per call. Tests group-commit concurrency design, a crash-safe on-disk record format, and backpressure reasoning.
You're building telemetry collection for a long-running batch-processing service. A single process spins up roughly 4,000 worker threads, each one periodically reporting small status records — think progress counters and timing samples — by calling into one shared component:
class TelemetrySink:
def __init__(self, path: str): ...
def record(self, payload: bytes) -> None: ...
Each payload is at most 4 KB and opaque: the sink never needs to parse or interpret it. All 4,000 threads share one TelemetrySink instance, and every call appends payload to one append-only file on local disk. The interviewer opens by asking you to describe how you'd build record, and to drive the conversation with your own questions before settling on a design.
The service currently targets roughly 50,000 record calls per second in aggregate across all threads, with a p99 latency budget per call of under 10 milliseconds, even though an fsync on the target disk can take several milliseconds on its own. A nightly reconciliation job reads the file after the process exits (or after a crash) and must be able to recover every complete record, cope with a possibly-corrupt final record, and tell records apart without any out-of-band index.
What you should cover
- A precise durability contract: exactly what must be true about a record once
recordreturns, and what happens to records written just before a crash. - A concurrency design that lets many threads' calls share a small number of expensive disk operations, instead of paying one
fsyncper call. - An on-disk record format that survives a crash mid-write: the reconciliation job must be able to walk the file, recover every intact record, and detect (not necessarily recover) a partially written final record.
- How backpressure works when callers produce records faster than the disk can absorb them, and what a slow or failing disk does to
record's callers. - Rough numbers: how big a batch, how long callers might wait, and how that interacts with the latency budget above.
Clarifying questions worth raising
- Does "durable" here mean safe against an OS crash or power loss, or is handing the bytes to the kernel's page cache enough?
- Do records from different threads need any particular relative ordering in the file, or just internal consistency per thread?
- Can
recordever block the caller, and for how long, under normal load versus a struggling disk? - Is the file rotated or size-bounded, and does the sink need an explicit shutdown/flush call?
Hints
Hint 1
Work out what 'durable' has to guarantee before you design anything — that answer determines whether you need `fsync` at all and how often.
Hint 2
A naive design calls `fsync` once per `record` call; with 4,000 threads that's 4,000 syncs in flight. Look for a way to let one sync cover many callers that arrived while it was in progress.
Hint 3
Design the byte layout of a single record (length, checksum, payload) before worrying about threads — the reconciliation job's requirements constrain this layout directly.
Hint 4
Separate 'the write landed in a buffer' from 'the buffer hit disk' — different threads can be unblocked at each of those two moments, and that gap is where your latency budget is won or lost.
Follow-up questions
- How would this change if several processes on the same machine needed to append to the same file concurrently, not just threads within one process?
- What's the actual cost difference between `fsync` and `fdatasync` here, and does it matter for this record format?
- How would you size the buffer cap and flush interval from a real measurement of the target disk's fsync latency, rather than guessing?
- If the reconciliation job needs to process the file while the service is still running, what does that add to the design?
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.