49. Design a Connection Pool with an Internal Request Queue
Design a Connection Pool with an Internal Request Queue
Design an in-memory Connection Pool that maintains a fixed number of reusable connection objects.
Connections are indexed as integers: 0, 1, 2, ... capacity - 1.
Clients requests a connection using a requestId. If a free connection exists, it is assigned immediately. If no free connection exists, the request is placed into an internal queue and will wait until a connection becomes free.
The internal queue is FIFO (first-come-first-serve): whenever a connection is released, it is immediately assigned to the oldest queued request.
Method Signatures
- Constructor:
ConnectionPool(int capacity)
capacity is always between 1 and 5000.
- Acquire:
int acquireConnection(String requestId)
requestId is always non-empty and unique. It contains only characters from [a-z] and [0-9].
acquireConnection will never be called with a duplicate requestId.
- If a connection is available, assign it to the request and return its
connectionId.
- If multiple connections are available, assign the connection with the lowest index.
- If all connections are blocked, enqueue the request and return
-1. The request will be assigned a connection later when one becomes free.
- Release:
boolean releaseConnection(String requestId)
- Return
true if requestId is valid and currently holds a connection, and that connection is released.
- Otherwise, return
false.
- As soon as a connection is released, it is immediately assigned to the oldest request waiting in the internal queue (if any).
- Expire:
void expireRequest(String requestId)
- If
requestId is present in the internal request queue, remove it. No connection will ever be assigned to it.
- If the request is not in the queue or is currently holding a connection, do nothing.
- Query:
List<String> getRequestsWithConnection()
- Return a list of all requests that are currently holding a connection, along with the connection id.
- Each item must be formatted as:
"requestId-connectionId" (e.g. "request12-4").
- The list must be sorted in lexicographically ascending order by
requestId.
Examples
Example 1: Acquire when connections are free
ConnectionPool pool = new ConnectionPool(3); // connections: 0,1,2
pool.acquireConnection("a1"); // returns 0
pool.acquireConnection("a2"); // returns 1
pool.acquireConnection("a3"); // returns 2
pool.getRequestsWithConnection(); // ["a1-0", "a2-1", "a3-2"]
Example 2: Queue requests when pool is full, then assign on release (FIFO)
ConnectionPool pool = new ConnectionPool(2); // connections: 0,1
pool.acquireConnection("a1"); // returns 0
pool.acquireConnection("a2"); // returns 1
pool.acquireConnection("b1"); // returns -1 (queued)
pool.acquireConnection("b2"); // returns -1 (queued)
pool.releaseConnection("a1"); // returns true
// connection 0 is immediately assigned to oldest queued request "b1"
pool.getRequestsWithConnection(); // ["a2-1", "b1-0"]
Example 3: Expire a queued request
ConnectionPool pool = new ConnectionPool(1); // connection: 0
pool.acquireConnection("a1"); // returns 0
pool.acquireConnection("b1"); // returns -1 (queued)
pool.acquireConnection("b2"); // returns -1 (queued)
pool.expireRequest("b1"); // b1 removed from queue
pool.releaseConnection("a1"); // returns true
// connection 0 is assigned to oldest remaining queued request "b2"
pool.getRequestsWithConnection(); // ["b2-0"]
Example 4: Invalid release
ConnectionPool pool = new ConnectionPool(1);
pool.releaseConnection("x1"); // false (unknown request)
pool.acquireConnection("a1"); // 0
pool.releaseConnection("a1"); // true
pool.releaseConnection("a1"); // false (already released, not holding now)