@@ -1015,6 +1015,40 @@ async def worker():
10151015 conn = await pool .acquire (timeout = POOL_NOMINAL_TIMEOUT )
10161016 await pool .release (conn )
10171017
1018+ async def test_pool_release_after_protocol_abort (self ):
1019+ pool = await self .create_pool (min_size = 1 , max_size = 1 )
1020+
1021+ conn = await pool .acquire ()
1022+ raw_conn = conn ._con
1023+ holder = conn ._holder
1024+ terminated = asyncio .Event ()
1025+ conn .add_termination_listener (lambda _ : terminated .set ())
1026+ self .assertEqual (await conn .fetchval ('SELECT 1' ), 1 )
1027+ stmt = next (iter (raw_conn ._stmt_cache .iter_statements ()))
1028+
1029+ raw_conn ._protocol .abort ()
1030+
1031+ await pool .release (conn )
1032+
1033+ # Releasing an aborted connection must finish connection cleanup.
1034+ await asyncio .wait_for (terminated .wait (), timeout = 1.0 )
1035+ self .assertTrue (stmt .closed )
1036+ self .assertEqual (len (raw_conn ._stmt_cache ), 0 )
1037+ self .assertIsNone (holder ._con )
1038+ self .assertIsNone (holder ._in_use )
1039+ self .assertIsNone (conn ._con )
1040+
1041+ # Repeated release must not return the holder to the queue twice.
1042+ await pool .release (conn )
1043+ self .assertEqual (pool ._queue .qsize (), 1 )
1044+
1045+ # The holder must reconnect and support queries after the abort.
1046+ conn2 = await pool .acquire (timeout = 1.0 )
1047+ self .assertIsNot (conn2 ._con , raw_conn )
1048+ self .assertEqual (await conn2 .fetchval ('SELECT 1' ), 1 )
1049+ await pool .release (conn2 )
1050+ await pool .close ()
1051+
10181052
10191053@unittest .skipIf (os .environ .get ('PGHOST' ), 'unmanaged cluster' )
10201054class TestPoolReconnectWithTargetSessionAttrs (tb .ClusterTestCase ):
0 commit comments