Implement a Database Connection Pool
HarveySoftware Engineer · OnsiteLast updated
Tests your ability to implement a thread-safe connection pool that manages connection lifecycles, blocking acquisition with timeouts, and resource reuse under concurrent access.
Problem Statement
Design and implement a thread-safe ConnectionPool that manages a fixed maximum number of database connections. In high-concurrency environments, creating connections on demand is expensive, while unbounded connections can overwhelm the database. Your pool must manage connection reuse, validate connection health, and handle blocking callers gracefully when the pool is exhausted.
Assume a Connection object provides the following interface:
class Connection:
def is_valid(self) -> bool:
"""Returns True if the connection is alive, False otherwise."""
pass
def close(self) -> None:
"""Closes the underlying connection resource."""
pass
Implement the ConnectionPool class with the following signature:
from typing import Callable, Optional
class ConnectionPool:
def __init__(self, max_connections: int, factory: Callable[[], Connection]):
"""
Initialize the pool with a maximum capacity and a factory callable
that creates a new Connection instance when needed.
"""
pass
def acquire(self, timeout: Optional[float] = None) -> Connection:
"""
Retrieves a valid connection from the pool. If no connection is
available and max_connections has not been reached, create a new one.
If max_connections has been reached, block until a connection is returned
or the specified timeout (in seconds) expires. If timeout expires,
raise a TimeoutError.
If a retrieved connection is not valid, discard it and obtain/create
another one.
"""
pass
def release(self, conn: Connection) -> None:
"""
Returns a connection back to the pool. If the connection is no longer
valid, close it and allow a new connection to be created in the future.
"""
pass
def close_all(self) -> None:
"""
Closes all idle connections and shuts down the pool. Any subsequent
call to acquire() should raise a RuntimeError.
"""
pass
Example Usage
pool = ConnectionPool(max_connections=2, factory=create_db_connection)
# Thread 1
conn1 = pool.acquire(timeout=1.0)
# Thread 2
conn2 = pool.acquire(timeout=1.0)
# Thread 3 attempts to acquire; blocks until conn1 or conn2 is released
# If no connection is released within 0.5s, raises TimeoutError
try:
conn3 = pool.acquire(timeout=0.5)
except TimeoutError:
print("Timed out waiting for connection")
pool.release(conn1)
conn3 = pool.acquire(timeout=0.5) # Now succeeds, receiving conn1
Constraints & Edge Cases
1 <= max_connections <= 1000- Multiple threads may call
acquire,release, andclose_allconcurrently. - Do not create all
max_connectionsupfront; instantiate them lazily as needed. - A thread releasing an already-invalid connection should not cause the pool to permanently lose capacity.
- Calling
releasewith a connection not managed by the pool or returning the same connection twice should be guarded against.
Hints
Hint 1
Use a Condition variable or a Semaphore paired with a Lock to manage thread blocking and notification when connections are returned.
Hint 2
Track both available idle connections and the total count of created connections so you know when to call the factory versus when to wait.
Hint 3
Validate connections before returning them from `acquire`; if `conn.is_valid()` is False, close it, decrement your total count, and retry.
Follow-up questions
- How would you implement idle-connection pruning (closing connections that have remained unused for longer than a TTL)?
- How can you implement context manager support (`with pool.connection() as conn:`) to ensure connections are automatically released even if exceptions occur?
- If many threads are waiting to acquire a connection, how do you prevent thread starvation and ensure fair (FIFO) acquisition?
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.