]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Allow pools to have min_size = 0 as long as they can grow
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Thu, 6 Jan 2022 21:16:09 +0000 (22:16 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Thu, 6 Jan 2022 21:44:03 +0000 (22:44 +0100)
Add tests to verify they can grow from 0 no problem.

docs/news_pool.rst
psycopg_pool/psycopg_pool/base.py
tests/pool/test_pool.py
tests/pool/test_pool_async.py

index dd055345ee99470c23e376af2c3d63caf7ce612a..7cfed1d91985cbf1f1944cfeb0285af229e2353f 100644 (file)
@@ -20,9 +20,9 @@ psycopg_pool 3.1.0 (unreleased)
 psycopg_pool 3.0.3 (unreleased)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-- Throw `!ValueError` if the pool `!min_size` is set to 0 (instead of
-  hanging).
-- Throw `!PoolClosed` calling `~ConnectionPool.wait()` on a closed pool.
+- Throw `!ValueError` if `ConnectionPool` `!min_size` and `!max_size` are both
+  set to 0 (instead of hanging).
+- Throw `PoolClosed` calling `~ConnectionPool.wait()` on a closed pool.
 
 
 Current release
index 251256a5746b1adab9f07178d4b810a508206874..7c9d96223d7a5de5fe915c23d585c88b651e6019 100644 (file)
@@ -121,13 +121,17 @@ class BasePool(Generic[ConnectionType]):
     def _check_size(
         self, min_size: int, max_size: Optional[int]
     ) -> Tuple[int, int]:
-        if min_size <= 0:
-            raise ValueError("min_size must be greater than 0")
+        if min_size < 0:
+            raise ValueError("min_size cannot be negative")
 
         if max_size is None:
             max_size = min_size
         if max_size < min_size:
             raise ValueError("max_size must be greater or equal than min_size")
+        if min_size == max_size == 0:
+            raise ValueError(
+                "if min_size is 0 max_size must be greater or than 0"
+            )
 
         return min_size, max_size
 
index 1a57367bf91da9c189b4e65fae1d3adfccb347ca..14297cdbf0a5791756cc889b43a432a3f4d8aa29 100644 (file)
@@ -40,16 +40,16 @@ def test_defaults(dsn):
         assert p.num_workers == 3
 
 
-def test_min_size_max_size(dsn):
-    with pool.ConnectionPool(dsn, min_size=2) as p:
-        assert p.min_size == p.max_size == 2
-
-    with pool.ConnectionPool(dsn, min_size=2, max_size=4) as p:
-        assert p.min_size == 2
-        assert p.max_size == 4
+@pytest.mark.parametrize("min_size, max_size", [(2, None), (0, 2), (2, 4)])
+def test_min_size_max_size(dsn, min_size, max_size):
+    with pool.ConnectionPool(dsn, min_size=min_size, max_size=max_size) as p:
+        assert p.min_size == min_size
+        assert p.max_size == max_size if max_size is not None else min_size
 
 
-@pytest.mark.parametrize("min_size, max_size", [(0, 0), (-1, None), (4, 2)])
+@pytest.mark.parametrize(
+    "min_size, max_size", [(0, 0), (0, None), (-1, None), (4, 2)]
+)
 def test_bad_size(dsn, min_size, max_size):
     with pytest.raises(ValueError):
         pool.ConnectionPool(min_size=min_size, max_size=max_size)
@@ -762,31 +762,40 @@ def test_reopen(dsn):
 
 @pytest.mark.slow
 @pytest.mark.timing
-def test_grow(dsn, monkeypatch, retries):
+@pytest.mark.parametrize(
+    "min_size, want_times",
+    [
+        (2, [0.25, 0.25, 0.35, 0.45, 0.50, 0.50, 0.60, 0.70]),
+        (0, [0.35, 0.45, 0.55, 0.60, 0.65, 0.70, 0.80, 0.85]),
+    ],
+)
+def test_grow(dsn, monkeypatch, retries, min_size, want_times):
     delay_connection(monkeypatch, 0.1)
 
     def worker(n):
         t0 = time()
         with p.connection() as conn:
-            conn.execute("select 1 from pg_sleep(0.2)")
+            conn.execute("select 1 from pg_sleep(0.25)")
         t1 = time()
         results.append((n, t1 - t0))
 
     for retry in retries:
         with retry:
             with pool.ConnectionPool(
-                dsn, min_size=2, max_size=4, num_workers=3
+                dsn, min_size=min_size, max_size=4, num_workers=3
             ) as p:
                 p.wait(1.0)
                 results: List[Tuple[int, float]] = []
 
-                ts = [Thread(target=worker, args=(i,)) for i in range(6)]
+                ts = [
+                    Thread(target=worker, args=(i,))
+                    for i in range(len(want_times))
+                ]
                 for t in ts:
                     t.start()
                 for t in ts:
                     t.join()
 
-            want_times = [0.2, 0.2, 0.3, 0.4, 0.4, 0.4]
             times = [item[1] for item in results]
             for got, want in zip(times, want_times):
                 assert got == pytest.approx(want, 0.1), times
index 4d37bef77c9675528bf14b60c7b0c853a7c9aadf..7d75d7ba6e8dc4742597fcb8726fae6723f753b2 100644 (file)
@@ -35,16 +35,18 @@ async def test_defaults(dsn):
         assert p.num_workers == 3
 
 
-async def test_min_size_max_size(dsn):
-    async with pool.AsyncConnectionPool(dsn, min_size=2) as p:
-        assert p.min_size == p.max_size == 2
-
-    async with pool.AsyncConnectionPool(dsn, min_size=2, max_size=4) as p:
-        assert p.min_size == 2
-        assert p.max_size == 4
+@pytest.mark.parametrize("min_size, max_size", [(2, None), (0, 2), (2, 4)])
+async def test_min_size_max_size(dsn, min_size, max_size):
+    async with pool.AsyncConnectionPool(
+        dsn, min_size=min_size, max_size=max_size
+    ) as p:
+        assert p.min_size == min_size
+        assert p.max_size == max_size if max_size is not None else min_size
 
 
-@pytest.mark.parametrize("min_size, max_size", [(0, 0), (-1, None), (4, 2)])
+@pytest.mark.parametrize(
+    "min_size, max_size", [(0, 0), (0, None), (-1, None), (4, 2)]
+)
 async def test_bad_size(dsn, min_size, max_size):
     with pytest.raises(ValueError):
         pool.AsyncConnectionPool(min_size=min_size, max_size=max_size)
@@ -738,29 +740,35 @@ async def test_reopen(dsn):
 
 @pytest.mark.slow
 @pytest.mark.timing
-async def test_grow(dsn, monkeypatch, retries):
+@pytest.mark.parametrize(
+    "min_size, want_times",
+    [
+        (2, [0.25, 0.25, 0.35, 0.45, 0.50, 0.50, 0.60, 0.70]),
+        (0, [0.35, 0.45, 0.55, 0.60, 0.65, 0.70, 0.80, 0.85]),
+    ],
+)
+async def test_grow(dsn, monkeypatch, retries, min_size, want_times):
     delay_connection(monkeypatch, 0.1)
 
     async def worker(n):
         t0 = time()
         async with p.connection() as conn:
-            await conn.execute("select 1 from pg_sleep(0.2)")
+            await conn.execute("select 1 from pg_sleep(0.25)")
         t1 = time()
         results.append((n, t1 - t0))
 
     async for retry in retries:
         with retry:
             async with pool.AsyncConnectionPool(
-                dsn, min_size=2, max_size=4, num_workers=3
+                dsn, min_size=min_size, max_size=4, num_workers=3
             ) as p:
                 await p.wait(1.0)
                 ts = []
                 results: List[Tuple[int, float]] = []
 
-                ts = [create_task(worker(i)) for i in range(6)]
+                ts = [create_task(worker(i)) for i in range(len(want_times))]
                 await asyncio.gather(*ts)
 
-            want_times = [0.2, 0.2, 0.3, 0.4, 0.4, 0.4]
             times = [item[1] for item in results]
             for got, want in zip(times, want_times):
                 assert got == pytest.approx(want, 0.1), times