]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
QueuePool: support subsecond timeout 5710/head
authorJordan Pittier <jordan@gorgias.io>
Wed, 18 Nov 2020 10:23:12 +0000 (11:23 +0100)
committerJordan Pittier <jordan@gorgias.io>
Wed, 18 Nov 2020 14:44:17 +0000 (15:44 +0100)
Make it explicit in the documentation and in the default value for the 'timeout'
parameter that `timeout` can be a float. Because Python timing is not
very accurate, warn about the precision.

lib/sqlalchemy/engine/create.py
lib/sqlalchemy/pool/impl.py
test/engine/test_pool.py

index 7f5b5e8f5deb6054cc0beac5ee2b5a1702c7c8e6..1aa9f12ccfbac88d61be9e064b9209268b76d4f6 100644 (file)
@@ -440,9 +440,11 @@ def create_engine(url, **kwargs):
 
             :paramref:`_pool.Pool.reset_on_return`
 
-    :param pool_timeout=30: number of seconds to wait before giving
+    :param pool_timeout=30.0: number of seconds to wait before giving
         up on getting a connection from the pool. This is only used
-        with :class:`~sqlalchemy.pool.QueuePool`.
+        with :class:`~sqlalchemy.pool.QueuePool`. This can be a float but is
+        subject to the limitations of Python time functions which may not be
+        reliable in the tens of milliseconds.
 
     :param pool_use_lifo=False: use LIFO (last-in-first-out) when retrieving
         connections from :class:`.QueuePool` instead of FIFO
index fc543053d31b5b53d1eced1893f07e375b0435aa..38afbc7a1a37a6090e17831439bd1670c76aaae6 100644 (file)
@@ -40,7 +40,7 @@ class QueuePool(Pool):
         creator,
         pool_size=5,
         max_overflow=10,
-        timeout=30,
+        timeout=30.0,
         use_lifo=False,
         **kw
     ):
@@ -73,7 +73,9 @@ class QueuePool(Pool):
           connections. Defaults to 10.
 
         :param timeout: The number of seconds to wait before giving up
-          on returning a connection. Defaults to 30.
+          on returning a connection. Defaults to 30.0. This can be a float
+          but is subject to the limitations of Python time functions which
+          may not be reliable in the tens of milliseconds.
 
         :param use_lifo: use LIFO (last-in-first-out) when retrieving
           connections instead of FIFO (first-in-first-out). Using LIFO, a
@@ -129,7 +131,7 @@ class QueuePool(Pool):
             else:
                 raise exc.TimeoutError(
                     "QueuePool limit of size %d overflow %d reached, "
-                    "connection timed out, timeout %d"
+                    "connection timed out, timeout %0.2f"
                     % (self.size(), self.overflow(), self._timeout),
                     code="3o7r",
                 )
index eb705da61abe88ca3d1eab20506f52f4e9289b95..bf08528f22309ddd2f4b56c062db64da098a0ccd 100644 (file)
@@ -4,6 +4,8 @@ import threading
 import time
 import weakref
 
+import pytest
+
 import sqlalchemy as tsa
 from sqlalchemy import event
 from sqlalchemy import pool
@@ -866,6 +868,17 @@ class QueuePoolTest(PoolTestBase):
         assert_raises(tsa.exc.TimeoutError, p.connect)
         assert int(time.time() - now) == 2
 
+    @testing.requires.timing_intensive
+    def test_timeout_subsecond_precision(self):
+        p = self._queuepool_fixture(pool_size=1, max_overflow=0, timeout=0.5)
+        c1 = p.connect()  # noqa
+        with pytest.raises(tsa.exc.TimeoutError, match=r".* timeout 0.50 .*"):
+            now = time.time()
+            c2 = p.connect()  # noqa
+        # Python timing is not very accurate, the time diff should be very
+        # close to 0.5s but we give 200ms of slack.
+        assert 0.3 <= time.time() - now <= 0.7, "Pool timeout not respected"
+
     @testing.requires.threading_with_mock
     @testing.requires.timing_intensive
     def test_timeout_race(self):