Skip to content

Connection pool soft invalidation uses time.time() comparison that can silently fail on low-resolution clocks (e.g. Windows ~16 ms) #13169

Description

@tejas-ae

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:

  1. Connection A is created — starttime = time.time() → say T
  2. soft_invalidate() is called in the same timer quantum → _soft_invalidate_time = time.time() → also T
  3. 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:

  1. 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?
  2. Would time.monotonic() be more appropriate here to avoid wall-clock regression issues in addition to the precision concern?
  3. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    connection pooluse casenot really a feature or a bug; can be support for new DB features or user use cases not anticipated

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions