Connection Pooling API Reference
API documentation for the connection pooling module.
aio_azure_clients_toolbox.connection_pooling
connection_pooling.py
The idea for our Azure clients is that unlike database connections all of our clients can be reused across multiple requesters.
What we really need to achieve is the following:
- A "pool" of connections, where
- Each connection may be shared by more than 1 requester, and
- Each connection has an idle lifespan.
The latter is the most important because Azure will enforce idle timeouts for all sockets. For this reason, we will do the following: - lazily create connections as needed - share connections between many requesters - put connections back into an idle data structure when necessary - when connections are dead (exception or idle timeout) then we'll lock and recreate - when a connection has exceeded its "share" amount, we'll lock and create a new one.
ConnectionPool(connector, client_limit=DEFAULT_SHARED_TRANSPORT_CLIENT_LIMIT, max_size=DEFAULT_MAX_SIZE, max_idle_seconds=DEFAULT_CONNECTION_MAX_IDLE_SECONDS, max_lifespan_seconds=None)
Our goal here is to allow many clients to share connections, but to expire them when they've reached their idle time limits.
Most clients can call this with the default values below.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connector
|
AbstractorConnector
|
An instance of an AbstractConnector for creating connections. |
required |
client_limit
|
int
|
The max clients allowed per connection (default: 100). |
DEFAULT_SHARED_TRANSPORT_CLIENT_LIMIT
|
max_size
|
int
|
The max size for the connection pool or max connections held (default: 10). |
DEFAULT_MAX_SIZE
|
max_idle_seconds
|
int
|
Maximum duration allowed for an idle connection before recylcing it. |
DEFAULT_CONNECTION_MAX_IDLE_SECONDS
|
max_lifespan_seconds
|
int | None
|
Optional setting which controls how long a connection live before recycling. |
None
|
Source code in aio_azure_clients_toolbox/connection_pooling.py
closeall()
async
expire_conn(connection)
async
Expire a connection. Because we yield AbstractConnections while our pool is SharedTransportConnections, we need to give clients a way to look up a connection and expire it directly.
Source code in aio_azure_clients_toolbox/connection_pooling.py
get(timeout=60.0, acquire_timeout=10.0)
async
Pull out an idle connection.
The binary heap allows us to always pull out the youngest connection, which is the one most likely to connect without issues. This relies on the less-than/greater-than implementation above.
Throws: ConnectionsExhausted if too many connections opened.
Source code in aio_azure_clients_toolbox/connection_pooling.py
SharedTransportConnection(connector, client_limit=DEFAULT_SHARED_TRANSPORT_CLIENT_LIMIT, max_lifespan_seconds=None, max_idle_seconds=DEFAULT_CONNECTION_MAX_IDLE_SECONDS)
Each connection can be shared by many clients.
The problem we need to solve for most pressingly is idle timeouts, but we also have problems around opening, establishing, and closing connections.
Thus, each connection has the following lifecycle phases:
- Closed
- Open and not ready
- Open and ready
These are also the critical sections of work, so transitioning from one phase to another involves locking the resource.
The problem is that when a client first attempts to use a connection, it calls
one of the network-communication methods, and at that point, the connection
is established. To other clients who are awaiting their turn, the connection
already looks open, so they may try to use it early and fail. The same problem
happens on closing: one client closes while another still thinks the connection is live.
Outside of this, after we have sent for the first time, we're fine to share the connection as much as we want. Thus, we need to lock out all clients during its critical sections of work:
- Lock this connection when opening an underlying connection
- Lock this connection when establishing "readiness" (first usage)
- Lock this connection when closing an underlying connection
At all other points we can share it a whole lot (between 100 clients or more). To see what's happening, enable debug logs: logger.getLogger("aio_azure_clients_toolbox").setLevel(logging.DEBUG)
Footnote: most Azure sdk clients use aiohttp shared transports below the surface which actually has threadpooling with up to 100 connections. We wanted something more generic, though, which is why this class exists.
Azure Python SDK has an example of a shared transport for Azure clients but we wanted to start simpler and more agnostic here:
https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/samples/example_shared_transport_async.py#L51-L71
Based on the Azure example, though, we should easily be able to share one of these "connection" objects with 100 requesters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connector
|
AbstractorConnector
|
An instance of an AbstractConnector for creating connections. |
required |
client_limit
|
int
|
The max clients allowed per connection (default: 100). |
DEFAULT_SHARED_TRANSPORT_CLIENT_LIMIT
|
max_idle_seconds
|
int
|
Maximum duration allowed for an idle connection before recylcing it. |
DEFAULT_CONNECTION_MAX_IDLE_SECONDS
|
max_lifespan_seconds
|
int | None
|
Optional setting which controls how long a connection live before recycling. |
None
|
Source code in aio_azure_clients_toolbox/connection_pooling.py
available
property
Check if connection exists and client usage limit has been reached
closeable
property
Check if connection can be closed (no clients using it)
expired
property
Calculate if connection has been idle or active longer than allowed
is_ready
property
Proxy for whether our readiness Event has been set.
lifetime
property
Check the lifetime of this object (in nanos)
should_close
property
Check if connection should be closed
time_spent_idle
property
Check the idle time of this object (in nanos)
acquire(timeout=10.0)
async
Acquire a connection with a timeout
Source code in aio_azure_clients_toolbox/connection_pooling.py
check_readiness()
async
Indicates when ready by waiting for the connector to signal
Source code in aio_azure_clients_toolbox/connection_pooling.py
checkin(conn=None)
async
Called after a connection has been used
Source code in aio_azure_clients_toolbox/connection_pooling.py
checkout()
async
This function has the important job of keeping
track of last_idle_start and making sure a connection has been
established and that it is ready.
Must be followed by checkin!
Source code in aio_azure_clients_toolbox/connection_pooling.py
close()
async
Closes the connection
Source code in aio_azure_clients_toolbox/connection_pooling.py
create()
async
Establishes the connection or reuses existing if already created.
Source code in aio_azure_clients_toolbox/connection_pooling.py
AbstractorConnector
Bases: ABC
AbstractConnection
Bases: ABC
ConnectionsExhausted
Bases: ValueError
ConnectionFailed
Bases: ConnectionError