@@ -17,218 +17,96 @@ generic-connection-pool
1717.. image :: https://codecov.io/gh/dapper91/generic-connection-pool/branch/master/graph/badge.svg
1818 :target: https://codecov.io/gh/dapper91/generic-connection-pool
1919 :alt: Code coverage
20+ .. image :: https://readthedocs.org/projects/generic-connection-pool/badge/?version=stable&style=flat
21+ :alt: ReadTheDocs status
22+ :target: https://generic-connection-pool.readthedocs.io/en/stable/
2023
2124
2225``generic-connection-pool `` is a connection pool that can be used for TCP, http, database connections.
2326
24- Features:
27+ Features
28+ --------
2529
2630- **generic nature **: can be used for any connection you desire (TCP, http, database)
2731- **runtime agnostic **: synchronous and asynchronous pool supported
2832- **flexibility **: flexable connection retention and recycling policy
2933- **fully-typed **: mypy type-checker compatible
3034
3135
32- Installation
33- ------------
36+ Getting started
37+ ---------------
3438
35- You can install generic-connection-pool with pip :
39+ Connection pool supports the following configurations :
3640
37- .. code-block :: console
41+ * **background_collector **: if ``True `` starts a background worker that disposes expired and idle connections
42+ maintaining requested pool state. If ``False `` the connections will be disposed on each connection release.
43+ * **dispose_batch_size **: maximum number of expired and idle connections to be disposed on connection release
44+ (if background collector is started the parameter is ignored).
45+ * **idle_timeout **: inactivity time (in seconds) after which an extra connection will be disposed
46+ (a connection considered as extra if the number of endpoint connection exceeds ``min_idle ``).
47+ * **max_lifetime **: number of seconds after which any connection will be disposed.
48+ * **min_idle **: minimum number of connections in each endpoint the pool tries to hold. Connections that exceed
49+ that number will be considered as extra and disposed after ``idle_timeout `` seconds of inactivity.
50+ * **max_size **: maximum number of endpoint connections.
51+ * **total_max_size **: maximum number of all connections in the pool.
3852
39- $ pip install generic-connection-pool
40-
41-
42- Quickstart
43- ----------
44-
45- The following example illustrates how to create asynchronous ssl socket pool:
53+ The following example illustrates how to create https pool:
4654
4755.. code-block :: python
4856
49- import asyncio
57+ import socket
58+ import ssl
59+ import urllib.parse
60+ from http.client import HTTPResponse
5061 from typing import Tuple
5162
52- from generic_connection_pool.asyncio import ConnectionPool
53- from generic_connection_pool.contrib.socket_async import TcpStreamConnectionManager
63+ from generic_connection_pool.contrib.socket import SslSocketConnectionManager
64+ from generic_connection_pool.threading import ConnectionPool
5465
5566 Hostname = str
5667 Port = int
5768 Endpoint = Tuple[Hostname, Port]
58- Connection = Tuple[asyncio.StreamReader, asyncio.StreamWriter]
59-
60-
61- async def main () -> None :
62- pool = ConnectionPool[Endpoint, Connection](
63- TcpStreamConnectionManager(ssl = True ),
64- idle_timeout = 30.0 ,
65- max_lifetime = 600.0 ,
66- min_idle = 3 ,
67- max_size = 20 ,
68- total_max_size = 100 ,
69- background_collector = True ,
70- )
71-
72- async with pool.connection(endpoint = (' www.wikipedia.org' , 443 ), timeout = 5.0 ) as (reader, writer):
73- request = (
74- ' GET / HTTP/1.0\n '
75- ' Host: www.wikipedia.org\n '
76- ' \n '
77- ' \n '
78- )
79- writer.write(request.encode())
80- await writer.drain()
81- response = await reader.read()
82-
83- print (response.decode())
84-
85- asyncio.run(main())
86-
87-
88- Configuration
89- -------------
90-
91- Synchronous and asynchronous pools supports the following parameters:
92-
93- * **connection_manager **: connection manager instance
94- * **acquire_timeout **: connection acquiring default timeout
95- * **dispose_batch_size **: number of connections to be disposed at once
96- (if background collector is started the parameter is ignored)
97- * **dispose_timeout **: connection disposal timeout
98- * **background_collector **: start worker that disposes timed-out connections in background maintain provided pool state
99- otherwise they will be disposed on each connection release
100- * **idle_timeout **: number of seconds after which a connection will be closed respecting min_idle parameter
101- (the connection will be closed only if the connection number exceeds min_idle)
102- * **max_lifetime **: number of seconds after which a connection will be closed (min_idle parameter will be ignored)
103- * **min_idle **: minimum number of connections the pool tries to hold (for each endpoint)
104- * **max_size **: maximum number of connections (for each endpoint)
105- * **total_max_size **: maximum number of connections (for all endpoints)
106-
107-
108- Generic nature
109- --------------
110-
111- Since the pool has generic nature is can be used for database connections as well:
112-
113- .. code-block :: python
114-
115- import psycopg2.extensions
116-
117- from generic_connection_pool.contrib.psycopg2 import DbConnectionManager
118- from generic_connection_pool.threding import ConnectionPool
119-
120- Endpoint = str
121- Connection = psycopg2.extensions.connection
122-
123-
124- def main () -> None :
125- dsn_params = dict (dbname = ' postgres' , user = ' postgres' , password = ' secret' )
69+ Connection = socket.socket
12670
127- pool = ConnectionPool[Endpoint, Connection](
128- DbConnectionManager(
129- dsn_params = {
130- ' master' : dict (dsn_params, host = ' db-master.local' ),
131- ' replica-1' : dict (dsn_params, host = ' db-replica-1.local' ),
132- ' replica-2' : dict (dsn_params, host = ' db-replica-2.local' ),
133- },
134- ),
135- acquire_timeout = 2.0 ,
136- idle_timeout = 60.0 ,
137- max_lifetime = 600.0 ,
138- min_idle = 3 ,
139- max_size = 10 ,
140- total_max_size = 15 ,
141- background_collector = True ,
142- )
14371
144- with pool.connection(endpoint = ' master' ) as conn:
145- cur = conn.cursor()
146- cur.execute(" SELECT * FROM pg_stats;" )
147- print (cur.fetchone())
72+ http_pool = ConnectionPool[Endpoint, Connection](
73+ SslSocketConnectionManager(ssl.create_default_context()),
74+ idle_timeout = 30.0 ,
75+ max_lifetime = 600.0 ,
76+ min_idle = 3 ,
77+ max_size = 20 ,
78+ total_max_size = 100 ,
79+ background_collector = True ,
80+ )
14881
149- with pool.connection(endpoint = ' replica-1' ) as conn:
150- cur = conn.cursor()
151- cur.execute(" SELECT * FROM pg_stats;" )
152- print (cur.fetchone())
15382
154- pool.close()
83+ def fetch (url : str , timeout : float = 5.0 ) -> None :
84+ url = urllib.parse.urlsplit(url)
85+ port = url.port or 443 if url.scheme == ' https' else 80
15586
87+ with http_pool.connection(endpoint = (url.hostname, port), timeout = timeout) as sock:
88+ request = (
89+ ' GET {path} HTTP/1.1\r\n '
90+ ' Host: {host} \r\n '
91+ ' \r\n '
92+ ' \r\n '
93+ ).format(host = url.hostname, path = url.path)
15694
157- main()
158-
159-
160- Extendability
161- -------------
162-
163- If built-in connection managers are not suitable for your task the one can be easily created by yourself:
164-
165- .. code-block :: python
166-
167- import socket
168- from ssl import SSLContext, SSLSocket
169- from typing import Optional, Tuple
170-
171- from generic_connection_pool.threding import BaseConnectionManager, ConnectionPool
172-
173- Hostname = str
174- Port = int
175- SslEndpoint = Tuple[Hostname, Port]
176- Connection = SSLSocket
177-
178-
179- class SslSocketConnectionManager (BaseConnectionManager[SslEndpoint, Connection]):
180- """
181- SSL socket connection manager.
182- """
183-
184- def __init__ (self , ssl : SSLContext):
185- self ._ssl = ssl
186-
187- def create (self , endpoint : SslEndpoint, timeout : Optional[float ] = None ) -> Connection:
188- hostname, port = endpoint
189-
190- sock = self ._ssl.wrap_socket(socket.socket(type = socket.SOCK_STREAM ), server_hostname = hostname)
191- sock.settimeout(timeout)
192- sock.connect((hostname, port))
193-
194- return sock
195-
196- def dispose (self , endpoint : SslEndpoint, conn : Connection, timeout : Optional[float ] = None ) -> None :
197- conn.settimeout(timeout)
198- try :
199- conn.shutdown(socket.SHUT_RDWR )
200- except OSError :
201- pass
202-
203- conn.close()
204-
95+ sock.write(request.encode())
20596
206- def main () -> None :
207- pool = ConnectionPool[SslEndpoint, Connection](
208- SslSocketConnectionManager(ssl = SSLContext()),
209- idle_timeout = 30.0 ,
210- max_lifetime = 600.0 ,
211- min_idle = 3 ,
212- max_size = 20 ,
213- total_max_size = 100 ,
214- background_collector = True ,
215- )
97+ response = HTTPResponse(sock)
98+ response.begin()
99+ status, body = response.getcode(), response.read(response.length)
216100
217- with pool.connection(endpoint = (' www.wikipedia.org' , 443 ), timeout = 5.0 ) as sock:
218- request = (
219- ' GET / HTTP/1.0\n '
220- ' Host: www.wikipedia.org\n '
221- ' \n '
222- ' \n '
223- )
224- sock.write(request.encode())
225- response = []
226- while chunk := sock.recv():
227- response.append(chunk)
101+ print (status)
102+ print (body)
228103
229- print (b ' ' .join(response).decode())
230104
231- pool.close()
105+ try :
106+ fetch(' https://en.wikipedia.org/wiki/HTTP' ) # http connection is opened
107+ fetch(' https://en.wikipedia.org/wiki/Python_(programming_language)' ) # http connection is reused
108+ finally :
109+ http_pool.close()
232110
233111
234- main()
112+ See ` documentation < https://generic-connection-pool.readthedocs.io/en/latest/ >`_ for more details.
0 commit comments