From 82019e7c1b2d0accae08525872b7a9a90f73edea Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:16:16 +0530 Subject: [PATCH] fix(credential_pool): unpack the tuple in next_available_at's gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- agent/credential_pool.py | 3 ++- .../test_reset_aware_primary_restore.py | 20 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/agent/credential_pool.py b/agent/credential_pool.py index 3d1742e782..1022665087 100644 --- a/agent/credential_pool.py +++ b/agent/credential_pool.py @@ -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: diff --git a/tests/run_agent/test_reset_aware_primary_restore.py b/tests/run_agent/test_reset_aware_primary_restore.py index a97dac3d5a..efc071e996 100644 --- a/tests/run_agent/test_reset_aware_primary_restore.py +++ b/tests/run_agent/test_reset_aware_primary_restore.py @@ -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