fix(credential_pool): unpack the tuple in next_available_at's gate
Cross-PR interaction fix: #77714 (salvage of #71775) changed _available_entries to return (available, pending_refresh) while #77631 (salvage of #67642) added next_available_at() which still truthiness- tests the bare return. A non-empty tuple is always truthy — even ([], []) — so the reset-aware gate silently returned None ('no wait info') for every exhausted pool, disabling the feature #77631 shipped. Unpack the tuple and test the available list. Also adapts the lock-probe test for the RLock introduced by #77714 (same-thread non-blocking acquire always succeeds on an RLock; probe from a helper thread instead).
This commit is contained in:
@@ -642,7 +642,8 @@ class CredentialPool:
|
||||
``_available_entries`` caller (see the comment on ``has_available``).
|
||||
"""
|
||||
with self._lock:
|
||||
if self._available_entries():
|
||||
available, _pending = self._available_entries()
|
||||
if available:
|
||||
return None
|
||||
candidates: List[float] = []
|
||||
for entry in self._entries:
|
||||
|
||||
@@ -203,9 +203,23 @@ class TestNextAvailableAt:
|
||||
original = pool._available_entries
|
||||
|
||||
def _probe(**kwargs):
|
||||
held["locked"] = not pool._lock.acquire(blocking=False)
|
||||
if not held["locked"]:
|
||||
pool._lock.release()
|
||||
# self._lock is an RLock (deferred-refresh mutations self-lock),
|
||||
# so a same-thread non-blocking acquire always succeeds; probe
|
||||
# ownership from a helper thread instead.
|
||||
import threading as _t
|
||||
|
||||
blocked = _t.Event()
|
||||
|
||||
def _try():
|
||||
if not pool._lock.acquire(blocking=False):
|
||||
blocked.set()
|
||||
else:
|
||||
pool._lock.release()
|
||||
|
||||
worker = _t.Thread(target=_try)
|
||||
worker.start()
|
||||
worker.join(timeout=5)
|
||||
held["locked"] = blocked.is_set()
|
||||
return original(**kwargs)
|
||||
|
||||
pool._available_entries = _probe
|
||||
|
||||
Reference in New Issue
Block a user