Design a lazy-initialized connection pool

Quick Overview

Design a thread-safe, lazily initialized connection pool with a PooledConnection wrapper: reuse idle connections first, cap active connections, define exhaustion (block-with-timeout) and shutdown semantics, and analyze concurrency safety and complexity. A common Airtable software-engineering screen question.

Design a lazy-initialized connection pool

Company: Airtable

Role: Software Engineer

Category: System Design

Difficulty: medium

Interview Round: HR Screen

##### Question Design and implement a thread-safe, lazily initialized connection pool. You are given an immutable base class `Connection` (you cannot modify it) that exposes `query()` and `close()`. Implement two classes: 1. **`ConnectionPool`** with `__init__(maxConnectionNum)`, `getConnection() -> PooledConnection`, and `close()`. 2. **`PooledConnection`** with `__init__(parent: ConnectionPool, connection: Connection)`, `query()`, and `close()`. Requirements: 1. **Lazy initialization** — do not create any `Connection` in the pool's constructor. Only create a new `Connection` instance when `getConnection()` is actually called. 2. **Reuse idle first** — when `getConnection()` is called, hand back an idle (already-created, returned) connection if one exists. Only create a new `Connection` when no idle connection is available *and* the number of active connections is below `maxConnectionNum`. 3. **Cap active connections** — the total number of underlying connections (in-use + idle) must never exceed `maxConnectionNum`. 4. **Exhaustion behavior** — define and implement what happens when the pool is exhausted (no idle connection and the cap is reached): block with a timeout, then raise an error on timeout (or block indefinitely if no timeout is configured). 5. **Return semantics** — `PooledConnection.close()` must *return* the underlying connection to the pool for reuse rather than actually closing it. `ConnectionPool.close()` must close all underlying connections and reject any further operations. 6. **`PooledConnection.query()`** — delegate to the underlying connection's `query()`, while guarding against use after the wrapper has been returned/closed. 7. **Concurrency safety** — discuss and implement how to make the pool safe for concurrent callers: which synchronization primitives to use, and how to avoid double-return and connection leaks. 8. **Complexity** — describe the core data structures and analyze the time/space complexity of `getConnection()` and `close()`.

Quick Answer: Design a thread-safe, lazily initialized connection pool with a PooledConnection wrapper: reuse idle connections first, cap active connections, define exhaustion (block-with-timeout) and shutdown semantics, and analyze concurrency safety and complexity. A common Airtable software-engineering screen question.

|Home/System Design/Airtable
Airtable logo
Airtable
Sep 6, 2025, 12:00 AM
mediumSoftware EngineerHR ScreenSystem Design
22
0
Question

Design and implement a thread-safe, lazily initialized connection pool.

You are given an immutable base class Connection (you cannot modify it) that exposes query() and close(). Implement two classes:

  1. ConnectionPool with __init__(maxConnectionNum) , getConnection() -> PooledConnection , and close() .
  2. PooledConnection with __init__(parent: ConnectionPool, connection: Connection) , query() , and close() .

Requirements:

  1. Lazy initialization — do not create any Connection in the pool's constructor. Only create a new Connection instance when getConnection() is actually called.
  2. Reuse idle first — when getConnection() is called, hand back an idle (already-created, returned) connection if one exists. Only create a new Connection when no idle connection is available and the number of active connections is below maxConnectionNum .
  3. Cap active connections — the total number of underlying connections (in-use + idle) must never exceed maxConnectionNum .
  4. Exhaustion behavior — define and implement what happens when the pool is exhausted (no idle connection and the cap is reached): block with a timeout, then raise an error on timeout (or block indefinitely if no timeout is configured).
  5. Return semantics PooledConnection.close() must return the underlying connection to the pool for reuse rather than actually closing it. ConnectionPool.close() must close all underlying connections and reject any further operations.
  6. PooledConnection.query() — delegate to the underlying connection's query() , while guarding against use after the wrapper has been returned/closed.
  7. Concurrency safety — discuss and implement how to make the pool safe for concurrent callers: which synchronization primitives to use, and how to avoid double-return and connection leaks.
  8. Complexity — describe the core data structures and analyze the time/space complexity of getConnection() and close() .

Submit Your Answer to Earn 20XP

Sign in to leave a comment

Loading comments...