From: Jordan Pittier Date: Wed, 18 Nov 2020 14:57:43 +0000 (-0500) Subject: QueuePool: support subsecond timeout X-Git-Tag: rel_1_4_0b2~138 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2b746211188642333b3151bfbb429b236a1559d1;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git QueuePool: support subsecond timeout Fixes: #5719 ### Description 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. ### Checklist This pull request is: - [ ] A documentation / typographical error fix - Good to go, no issue or tests are needed - [x] A short code fix - please include the issue number, and create an issue if none exists, which must include a complete example of the issue. one line code fixes without an issue and demonstration will not be accepted. - Please include: `Fixes: #` in the commit message - please include tests. one line code fixes without tests will not be accepted. - [ ] A new feature implementation - please include the issue number, and create an issue if none exists, which must include a complete example of how the feature would look. - Please include: `Fixes: #` in the commit message - please include tests. **Have a nice day!** Closes: #5710 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5710 Pull-request-sha: 5f4eef8b4aba756d32e14ea41f71ef2919c26b84 Change-Id: I462524b1624ca5cc76d083a1d58e5dc89501c1a9 --- diff --git a/doc/build/changelog/unreleased_14/5719.rst b/doc/build/changelog/unreleased_14/5719.rst new file mode 100644 index 0000000000..3f0d73e5d4 --- /dev/null +++ b/doc/build/changelog/unreleased_14/5719.rst @@ -0,0 +1,6 @@ +.. change:: + :tags: pool, tests, usecase + :tickets: 5582 + + Improve documentation and add test for sub-second pool timeouts. + Pull request courtesy Jordan Pittier. diff --git a/lib/sqlalchemy/engine/create.py b/lib/sqlalchemy/engine/create.py index 786f8f5d6a..b48cead791 100644 --- a/lib/sqlalchemy/engine/create.py +++ b/lib/sqlalchemy/engine/create.py @@ -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 diff --git a/lib/sqlalchemy/pool/impl.py b/lib/sqlalchemy/pool/impl.py index fc543053d3..38afbc7a1a 100644 --- a/lib/sqlalchemy/pool/impl.py +++ b/lib/sqlalchemy/pool/impl.py @@ -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", ) diff --git a/test/engine/test_pool.py b/test/engine/test_pool.py index 9ea3065b03..1a49cf4b92 100644 --- a/test/engine/test_pool.py +++ b/test/engine/test_pool.py @@ -14,6 +14,7 @@ from sqlalchemy.testing import assert_raises from sqlalchemy.testing import assert_raises_context_ok from sqlalchemy.testing import assert_raises_message from sqlalchemy.testing import eq_ +from sqlalchemy.testing import expect_raises from sqlalchemy.testing import fixtures from sqlalchemy.testing import is_ from sqlalchemy.testing import is_not @@ -886,6 +887,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 expect_raises(tsa.exc.TimeoutError): + 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):