]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
fix(pool): continue growing if the pool is not full yet
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 6 Sep 2022 23:49:14 +0000 (00:49 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 6 Sep 2022 23:58:47 +0000 (00:58 +0100)
Previously, starting from an empty pool, we would have stopped at the
first connection created.

psycopg_pool/psycopg_pool/pool.py
psycopg_pool/psycopg_pool/pool_async.py
tests/pool/test_pool.py
tests/pool/test_pool_async.py

index 0f3cf535acdff74d2acc7624091ff8b445943825..c3c9579ee002ee920a86b3afb16f1dc3335eda70 100644 (file)
@@ -83,7 +83,7 @@ class ConnectionPool(BasePool[Connection[Any]]):
 
         with self._lock:
             assert not self._pool_full_event
-            if len(self._pool) >= self._nconns:
+            if len(self._pool) >= self._min_size:
                 return
             self._pool_full_event = threading.Event()
 
@@ -543,7 +543,11 @@ class ConnectionPool(BasePool[Connection[Any]]):
         self._add_to_pool(conn)
         if growing:
             with self._lock:
-                if self._nconns < self._max_size and self._waiting:
+                # Keep on growing if the pool is not full yet, or if there are
+                # clients waiting and the pool can extend.
+                if self._nconns < self._min_size or (
+                    self._nconns < self._max_size and self._waiting
+                ):
                     self._nconns += 1
                     logger.info("growing pool %r to %s", self.name, self._nconns)
                     self.run_task(AddConnection(self, growing=True))
@@ -600,7 +604,7 @@ class ConnectionPool(BasePool[Connection[Any]]):
 
                 # If we have been asked to wait for pool init, notify the
                 # waiter if the pool is full.
-                if self._pool_full_event and len(self._pool) >= self._nconns:
+                if self._pool_full_event and len(self._pool) >= self._min_size:
                     self._pool_full_event.set()
 
     def _reset_connection(self, conn: Connection[Any]) -> None:
index e54d7b19aa9a4cbc4c2fb898df85cc430ee0aa85..11d7cef42f42142b78ac3abd7142856f00973308 100644 (file)
@@ -62,7 +62,7 @@ class AsyncConnectionPool(BasePool[AsyncConnection[Any]]):
 
         async with self._lock:
             assert not self._pool_full_event
-            if len(self._pool) >= self._nconns:
+            if len(self._pool) >= self._min_size:
                 return
             self._pool_full_event = asyncio.Event()
 
@@ -444,7 +444,11 @@ class AsyncConnectionPool(BasePool[AsyncConnection[Any]]):
         await self._add_to_pool(conn)
         if growing:
             async with self._lock:
-                if self._nconns < self._max_size and self._waiting:
+                # Keep on growing if the pool is not full yet, or if there are
+                # clients waiting and the pool can extend.
+                if self._nconns < self._min_size or (
+                    self._nconns < self._max_size and self._waiting
+                ):
                     self._nconns += 1
                     logger.info("growing pool %r to %s", self.name, self._nconns)
                     self.run_task(AddConnection(self, growing=True))
@@ -501,7 +505,7 @@ class AsyncConnectionPool(BasePool[AsyncConnection[Any]]):
 
                 # If we have been asked to wait for pool init, notify the
                 # waiter if the pool is full.
-                if self._pool_full_event and len(self._pool) >= self._nconns:
+                if self._pool_full_event and len(self._pool) >= self._min_size:
                     self._pool_full_event.set()
 
     async def _reset_connection(self, conn: AsyncConnection[Any]) -> None:
index 51d800a82e34a0125cf3ccca8a7bc27be39e7173..b83ca08e919a042ad0030d66002b759ffd2d9da8 100644 (file)
@@ -922,7 +922,9 @@ def test_reconnect_failure(proxy):
 
 
 @pytest.mark.slow
-def test_reconnect_retry(proxy):
+def test_reconnect_after_grow_failed(proxy):
+    # Retry reconnection after a failed connection attempt has put the pool
+    # in grow mode. See issue #370.
     proxy.stop()
 
     ev = Event()
@@ -931,7 +933,7 @@ def test_reconnect_retry(proxy):
         ev.set()
 
     with pool.ConnectionPool(
-        proxy.client_dsn, reconnect_timeout=1.0, reconnect_failed=failed
+        proxy.client_dsn, min_size=4, reconnect_timeout=1.0, reconnect_failed=failed
     ) as p:
         assert ev.wait(timeout=2)
 
@@ -947,6 +949,9 @@ def test_reconnect_retry(proxy):
         with p.connection(timeout=2) as conn:
             conn.execute("select 1")
 
+        p.wait(timeout=3.0)
+        assert len(p._pool) == p.min_size == 4
+
 
 @pytest.mark.slow
 def test_uniform_use(dsn):
index 6c923e63896096d208e6c841f299ec1ff0ce9684..7d6ca6d7942dd8c4479c41d4b85d72f963b311d9 100644 (file)
@@ -874,7 +874,9 @@ async def test_reconnect_failure(proxy):
 
 
 @pytest.mark.slow
-async def test_reconnect_retry(proxy):
+async def test_reconnect_after_grow_failed(proxy):
+    # Retry reconnection after a failed connection attempt has put the pool
+    # in grow mode. See issue #370.
     proxy.stop()
 
     ev = asyncio.Event()
@@ -883,7 +885,7 @@ async def test_reconnect_retry(proxy):
         ev.set()
 
     async with pool.AsyncConnectionPool(
-        proxy.client_dsn, reconnect_timeout=1.0, reconnect_failed=failed
+        proxy.client_dsn, min_size=4, reconnect_timeout=1.0, reconnect_failed=failed
     ) as p:
         await asyncio.wait_for(ev.wait(), 2.0)
 
@@ -899,6 +901,9 @@ async def test_reconnect_retry(proxy):
         async with p.connection(timeout=2) as conn:
             await conn.execute("select 1")
 
+        await p.wait(timeout=3.0)
+        assert len(p._pool) == p.min_size == 4
+
 
 @pytest.mark.slow
 async def test_uniform_use(dsn):