@@ -86,19 +86,24 @@ class PoolConnectionHolder:
8686
8787 __slots__ = ('_con' , '_pool' , '_loop' ,
8888 '_connect_args' , '_connect_kwargs' ,
89- '_max_queries' , '_setup' , '_init' )
89+ '_max_queries' , '_setup' , '_init' ,
90+ '_max_inactive_time' , '_in_use' ,
91+ '_inactive_callback' )
9092
9193 def __init__ (self , pool , * , connect_args , connect_kwargs ,
92- max_queries , setup , init ):
94+ max_queries , setup , init , max_inactive_time ):
9395
9496 self ._pool = pool
9597 self ._con = None
9698
9799 self ._connect_args = connect_args
98100 self ._connect_kwargs = connect_kwargs
99101 self ._max_queries = max_queries
102+ self ._max_inactive_time = max_inactive_time
100103 self ._setup = setup
101104 self ._init = init
105+ self ._inactive_callback = None
106+ self ._in_use = False
102107
103108 async def connect (self ):
104109 assert self ._con is None
@@ -134,6 +139,8 @@ async def acquire(self) -> PoolConnectionProxy:
134139 if self ._con is None :
135140 await self .connect ()
136141
142+ self ._maybe_cancel_inactive_callback ()
143+
137144 proxy = PoolConnectionProxy (self , self ._con )
138145
139146 if self ._setup is not None :
@@ -154,9 +161,12 @@ async def acquire(self) -> PoolConnectionProxy:
154161 self ._con = None
155162 raise ex
156163
164+ self ._in_use = True
157165 return proxy
158166
159167 async def release (self ):
168+ self ._in_use = False
169+
160170 if self ._con .is_closed ():
161171 self ._con = None
162172
@@ -181,7 +191,13 @@ async def release(self):
181191 self ._con = None
182192 raise ex
183193
194+ assert self ._inactive_callback is None
195+ if self ._max_inactive_time :
196+ self ._inactive_callback = self ._pool ._loop .call_later (
197+ self ._max_inactive_time , self ._deactivate_connection )
198+
184199 async def close (self ):
200+ self ._maybe_cancel_inactive_callback ()
185201 if self ._con is None :
186202 return
187203 if self ._con .is_closed ():
@@ -194,6 +210,7 @@ async def close(self):
194210 self ._con = None
195211
196212 def terminate (self ):
213+ self ._maybe_cancel_inactive_callback ()
197214 if self ._con is None :
198215 return
199216 if self ._con .is_closed ():
@@ -205,6 +222,18 @@ def terminate(self):
205222 finally :
206223 self ._con = None
207224
225+ def _maybe_cancel_inactive_callback (self ):
226+ if self ._inactive_callback is not None :
227+ self ._inactive_callback .cancel ()
228+ self ._inactive_callback = None
229+
230+ def _deactivate_connection (self ):
231+ assert not self ._in_use
232+ if self ._con is None or self ._con .is_closed ():
233+ return
234+ self ._con .terminate ()
235+ self ._con = None
236+
208237
209238class Pool :
210239 """A connection pool.
@@ -225,6 +254,7 @@ def __init__(self, *connect_args,
225254 min_size ,
226255 max_size ,
227256 max_queries ,
257+ max_inactive_connection_lifetime ,
228258 setup ,
229259 init ,
230260 loop ,
@@ -247,6 +277,11 @@ def __init__(self, *connect_args,
247277 if max_queries <= 0 :
248278 raise ValueError ('max_queries is expected to be greater than zero' )
249279
280+ if max_inactive_connection_lifetime < 0 :
281+ raise ValueError (
282+ 'max_inactive_connection_lifetime is expected to be greater '
283+ 'or equal to zero' )
284+
250285 self ._minsize = min_size
251286 self ._maxsize = max_size
252287
@@ -265,6 +300,7 @@ def __init__(self, *connect_args,
265300 connect_args = connect_args ,
266301 connect_kwargs = connect_kwargs ,
267302 max_queries = max_queries ,
303+ max_inactive_time = max_inactive_connection_lifetime ,
268304 setup = setup ,
269305 init = init )
270306
@@ -511,6 +547,7 @@ def create_pool(dsn=None, *,
511547 min_size = 10 ,
512548 max_size = 10 ,
513549 max_queries = 50000 ,
550+ max_inactive_connection_lifetime = 60.0 ,
514551 setup = None ,
515552 init = None ,
516553 loop = None ,
@@ -548,6 +585,9 @@ def create_pool(dsn=None, *,
548585 :param int max_size: Max number of connections in the pool.
549586 :param int max_queries: Number of queries after a connection is closed
550587 and replaced with a new connection.
588+ :param float max_inactive_connection_lifetime:
589+ Number of seconds after which inactive connections in the
590+ pool will be closed. Pass ``0`` to disable this mechanism.
551591 :param coroutine setup: A coroutine to prepare a connection right before
552592 it is returned from :meth:`~pool.Pool.acquire`.
553593 An example use case would be to automatically
@@ -567,7 +607,9 @@ def create_pool(dsn=None, *,
567607 An :exc:`~asyncpg.exceptions.InterfaceError` will be raised on any
568608 attempted operation on a released connection.
569609 """
570- return Pool (dsn ,
571- min_size = min_size , max_size = max_size ,
572- max_queries = max_queries , loop = loop , setup = setup , init = init ,
573- ** connect_kwargs )
610+ return Pool (
611+ dsn ,
612+ min_size = min_size , max_size = max_size ,
613+ max_queries = max_queries , loop = loop , setup = setup , init = init ,
614+ max_inactive_connection_lifetime = max_inactive_connection_lifetime ,
615+ ** connect_kwargs )
0 commit comments