In lib/sqlalchemy/pool/base.py, _ConnectionRecord.get_connection detects soft invalidation via:
# base.py, line 852
elif self._soft_invalidate_time > self.starttime:
do_connect = True
_soft_invalidate_time is set with time.time() (line 812) and starttime is also set with time.time() (line 893 in __connect). The code already acknowledges that time.time() can have ~16 ms granularity on Windows (comments at lines 820–831), but this caveat applies equally to soft invalidation.
Scenario where this can fail:
- Connection
A is created — starttime = time.time() → say T
soft_invalidate() is called in the same timer quantum → _soft_invalidate_time = time.time() → also T
- The check
T > T is False → the connection is not recycled even though it should be
This is the same class of issue as the hard invalidation check, but _soft_invalidate_time is only set to time.time() directly (no +1 or fuzz), whereas some backends use monotonic clocks or add epsilon values for robustness.
Questions:
- Should
_soft_invalidate_time be set to time.time() + 1e-6 (or similar) to guarantee > rather than == when created in the same timer tick as the connection?
- Would
time.monotonic() be more appropriate here to avoid wall-clock regression issues in addition to the precision concern?
- Is there a test that exercises soft invalidation with a mocked clock to catch this edge case?
File: lib/sqlalchemy/pool/base.py, lines 812, 852–858, 893.
In
lib/sqlalchemy/pool/base.py,_ConnectionRecord.get_connectiondetects soft invalidation via:_soft_invalidate_timeis set withtime.time()(line 812) andstarttimeis also set withtime.time()(line 893 in__connect). The code already acknowledges thattime.time()can have ~16 ms granularity on Windows (comments at lines 820–831), but this caveat applies equally to soft invalidation.Scenario where this can fail:
Ais created —starttime = time.time()→ sayTsoft_invalidate()is called in the same timer quantum →_soft_invalidate_time = time.time()→ alsoTT > TisFalse→ the connection is not recycled even though it should beThis is the same class of issue as the hard invalidation check, but
_soft_invalidate_timeis only set totime.time()directly (no+1or fuzz), whereas some backends use monotonic clocks or add epsilon values for robustness.Questions:
_soft_invalidate_timebe set totime.time() + 1e-6(or similar) to guarantee>rather than==when created in the same timer tick as the connection?time.monotonic()be more appropriate here to avoid wall-clock regression issues in addition to the precision concern?File:
lib/sqlalchemy/pool/base.py, lines 812, 852–858, 893.