Implement a Lazily Initialized, Thread-Safe Connection Pool
Context
You are given an immutable base class Connection with the following methods:
-
query(...): executes a query on the underlying resource
-
close(): closes the underlying resource
Design and implement a lazily initialized connection pool that hands out PooledConnection wrappers. The pool should create actual Connection instances only when needed, reuse them when possible, and handle exhaustion and shutdown correctly.
Assume you can construct new Connection objects via a provided factory function (e.g., connection_factory()) and that Connection is safe to use by a single thread at a time.
Requirements
Implement two classes with these methods:
-
class ConnectionPool
-
init
(maxConnectionNum, default_timeout=None)
-
getConnection() -> PooledConnection
-
close()
-
class PooledConnection
-
init
(parent: ConnectionPool, connection: Connection)
-
query(...)
-
close()
Functional requirements:
-
a) Lazy creation: Create a new Connection instance only when getConnection() is called and there are no idle connections available.
-
b) Reuse-first: Reuse idle connections before creating new ones. If none are idle and the number of active connections is below maxConnectionNum, create a new connection.
-
c) Exhaustion policy: Define behavior when the pool is exhausted. Acceptable options include blocking with a timeout or raising an error. Use a pool-level default timeout (default_timeout). If default_timeout is None, block indefinitely; otherwise, block up to default_timeout and raise a timeout error.
-
d) Closing semantics:
-
PooledConnection.close() returns the underlying connection to the pool instead of closing it.
-
ConnectionPool.close() must prevent future getConnection() calls, close all idle connections immediately, and eventually close all in-use connections as they are returned. The method may block until all borrowed connections are returned.
-
e) Concurrency safety: Make the pool safe for concurrent callers. Discuss and use appropriate synchronization primitives; prevent double-return of the same connection and resource leaks.
Also provide:
-
Core data structures used inside the pool.
-
Time and space complexity for getConnection() and close().