The skeleton made it look like a straightforward wrapper exercise but the real work was in the ownership semantics.
Start by clarifying requirements and constraints, then outline the design of ConnectionPool and PoolConnection with a focus on lifecycle and error handling. Walk through the implementation details, emphasizing reuse of idle connections and state management, and finally discuss trade-offs and edge cases.
Pro tip: Use a context manager (with statement) for PoolConnection to ensure connections are always returned to the pool, even if exceptions occur. This demonstrates robust resource management and simplifies client code.
Ask about expected pool size, concurrency model (thread-safe?), and whether connections should be validated before reuse. Confirm that DBConnection cannot be modified and that PoolConnection should wrap it.
Define ConnectionPool with a queue of idle connections and a set of in-use connections. PoolConnection wraps a DBConnection and holds a reference to the pool, with a state flag (e.g., closed) to prevent double-close and query-after-close.
In ConnectionPool, implement acquire() to return an idle connection or create a new one if under max size, and release() to return a connection to the pool. In PoolConnection, implement query() to delegate to the underlying connection if not closed, and close() to return the connection to the pool and mark as closed.
Raise appropriate exceptions for double-close (e.g., RuntimeError) and query after close. Ensure thread safety with locks if needed. Consider connection health checks and timeouts for acquiring connections.
Talk about trade-offs: pool size vs. resource usage, blocking vs. non-blocking acquire, and validation overhead. Mention testing strategies: unit tests for lifecycle, concurrency tests, and error cases.
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.