Thread-Safe Buffered Batch Writer for Streaming Records into a Database
DisneySoftware Engineer · Technical ScreenLast updated
Design and implement a thread-safe batch buffer that collects streaming records from multiple producer threads and flushes them to a database using batch inserts based on size, timeout, or shutdown triggers.
Problem Statement
In high-throughput event processing pipelines, inserting records into a database one by one creates excessive network overhead and connection contention. Instead, records are typically collected in memory and written out in batches.
Implement a thread-safe BatchWriter class that receives records from multiple concurrent producer threads, buffers them, and writes them to an external database client.
A batch must be flushed to the database when either of the following conditions is met:
- The number of buffered records reaches
batch_size. - More than
flush_interval_msmilliseconds have elapsed since the oldest record in the current batch was added.
Crucially, external database I/O is slow. Your design must not hold the main buffer lock while invoking db_client.insert_batch(...) so that producer threads are not blocked during network calls.
Class Interface
class DatabaseClient:
def insert_batch(self, records: list[dict]) -> None:
"""Writes a batch of records to the database. May take significant time."""
pass
class BatchWriter:
def __init__(self, db_client: DatabaseClient, batch_size: int, flush_interval_ms: int, max_capacity: int):
"""Initializes the writer with size and time thresholds, and backpressure limit."""
pass
def write(self, record: dict) -> None:
"""Adds a record to the buffer. If the buffer is full (max_capacity), blocks until space is available."""
pass
def flush(self) -> None:
"""Forces an immediate flush of all currently buffered records."""
pass
def close(self) -> None:
"""Flushes all remaining buffered records and terminates background timer threads cleanly."""
pass
Requirements and Constraints
batch_size >= 1,flush_interval_ms >= 10,max_capacity >= batch_size.write()must be thread-safe and handle backpressure: if total pending records reachmax_capacity, producer threads must block until records are drained.- Do not perform
db_client.insert_batchinside a critical section locking the incoming queue; snapshot or swap the batch under the lock, release the lock, and then write. - Calling
close()should prevent any further writes, flush all remaining in-flight records, and wait for any active batch inserts to finish.
Example Scenario
# Configuration: batch_size=3, flush_interval_ms=500
writer = BatchWriter(db_client, batch_size=3, flush_interval_ms=500, max_capacity=10)
# Thread A writes record 1 at t=0ms
# Thread B writes record 2 at t=10ms
# At t=500ms, timer expires -> db_client.insert_batch([rec1, rec2]) is executed
# Thread A writes records 3, 4, 5 in rapid succession at t=600ms
# Threshold reached -> db_client.insert_batch([rec3, rec4, rec5]) is executed immediately
writer.close() # Ensures no records are dropped
Edge Cases to Consider
close()called while producers are actively callingwrite().- Empty flushes triggered by timers when no records have arrived.
- Ensuring the background timer resets accurately after an eager size-based flush.
Hints
Hint 1
Consider decoupling ingestion from persistence by using a thread-safe blocking queue or swapping the active buffer with a fresh one inside a quick critical section.
Hint 2
A dedicated background flusher thread or a scheduled timer task can monitor the elapsed time since the oldest unflushed record without blocking active writers.
Hint 3
Use condition variables (`wait` and `notify`/`notify_all`) to implement the backpressure blocking logic when `max_capacity` is reached.
Follow-up questions
- How would you handle transient database write failures without losing records or violating the order of operations?
- If batches fail partially during insertion, how would you incorporate record deduplication or idempotency keys?
- How would you scale this design if single-threaded batch insertion cannot keep up with high producer throughput?
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.