diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-10-07 08:42:48 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-10-07 09:14:13 -0400 |
commit | 0220b58917b5a979891b5765f6ac5095e0368489 (patch) | |
tree | 2c914e75af50484b8e88603cfeab418e900fe4f2 /lib/sqlalchemy/pool | |
parent | fc643ae73009e91e2dcff9ef57fb7dc0e48df88b (diff) | |
download | sqlalchemy-0220b58917b5a979891b5765f6ac5095e0368489.tar.gz |
Use monotonic time for pool age measurement
The internal clock used by the :class:`_pool.Pool` object is now
time.monotonic_time() under Python 3. Under Python 2, time.time() is still
used, which is legacy. This clock is used to measure the age of a
connection against its starttime, and used in comparisons against the
pool_timeout setting as well as the last time the pool was marked as
invalid to determine if the connection should be recycled. Previously,
time.time() was used which was subject to inaccuracies as a result of
system clock changes as well as poor time resolution on windows.
Change-Id: I94f90044c1809508e26a5a00134981c2a00d0405
Diffstat (limited to 'lib/sqlalchemy/pool')
-rw-r--r-- | lib/sqlalchemy/pool/base.py | 26 |
1 files changed, 9 insertions, 17 deletions
diff --git a/lib/sqlalchemy/pool/base.py b/lib/sqlalchemy/pool/base.py index 87383fef7..2f1959f7c 100644 --- a/lib/sqlalchemy/pool/base.py +++ b/lib/sqlalchemy/pool/base.py @@ -11,16 +11,15 @@ """ from collections import deque -import time import weakref from .. import event from .. import exc from .. import log from .. import util +from ..util import monotonic_time from ..util import threading - reset_rollback = util.symbol("reset_rollback") reset_commit = util.symbol("reset_commit") reset_none = util.symbol("reset_none") @@ -261,7 +260,7 @@ class Pool(log.Identified): """ rec = getattr(connection, "_connection_record", None) if not rec or self._invalidate_time < rec.starttime: - self._invalidate_time = time.time() + self._invalidate_time = monotonic_time() if _checkin and getattr(connection, "is_valid", False): connection.invalidate(exception) @@ -510,7 +509,7 @@ class _ConnectionRecord(object): self.connection, ) if soft: - self._soft_invalidate_time = time.time() + self._soft_invalidate_time = monotonic_time() else: self.__close() self.connection = None @@ -519,23 +518,16 @@ class _ConnectionRecord(object): recycle = False # NOTE: the various comparisons here are assuming that measurable time - # passes between these state changes. however, time.time() is not - # guaranteed to have sub-second precision. comparisons of - # "invalidation time" to "starttime" should perhaps use >= so that the - # state change can take place assuming no measurable time has passed, - # however this does not guarantee correct behavior here as if time - # continues to not pass, it will try to reconnect repeatedly until - # these timestamps diverge, so in that sense using > is safer. Per - # https://stackoverflow.com/a/1938096/34549, Windows time.time() may be - # within 16 milliseconds accuracy, so unit tests for connection - # invalidation need a sleep of at least this long between initial start - # time and invalidation for the logic below to work reliably. + # passes between these state changes. on Python 2, we are still using + # time.time() which is not guaranteed to have millisecondsecond + # precision, i.e. on Windows. For Python 3 we now use monotonic_time(). + if self.connection is None: self.info.clear() self.__connect() elif ( self.__pool._recycle > -1 - and time.time() - self.starttime > self.__pool._recycle + and monotonic_time() - self.starttime > self.__pool._recycle ): self.__pool.logger.info( "Connection %r exceeded timeout; recycling", self.connection @@ -577,7 +569,7 @@ class _ConnectionRecord(object): # creator fails, this attribute stays None self.connection = None try: - self.starttime = time.time() + self.starttime = monotonic_time() connection = pool._invoke_creator(self) pool.logger.debug("Created new connection %r", connection) self.connection = connection |