mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-15 04:01:05 +00:00
b88b922db5
_probe_remote_gateway held _remote_probe_lock only around the cache read and cache write; the network probe itself (_http_probe per path, each up to ~2s) ran unlocked. On a cold cache a dashboard that fans out to N panels fired N concurrent probe sets at the (possibly dead) gateway, each blocking up to ~6s (#5455, #2476). Wrap the existing lock in a Condition and add an in-flight set: the first 'leader' thread marks the base_url in-flight and runs the probe (via extracted _run_remote_probe), while latecomers wait on the Condition for the leader's cached result instead of probing themselves. The leader clears the marker and notify_all()s in a finally block, so waiters are woken even if the probe raises (no deadlock); a bounded wait budget lets a waiter fall back to probing itself if a leader hangs. _reset_remote_probe_cache_for_tests also clears the in-flight set. Expire the cache from the probe's COMPLETION time, not the leader's entry time: walking every path of a hung gateway takes len(paths) * timeout (~6s), which exceeds the 5s TTL, so `entry_time + TTL` would store an already-expired cache line. Waiters woken right after would then miss the cache and each re-probe the dead gateway — collapsing single-flight and regressing latency to worse-than- serial. A regression test drives a walk longer than the TTL and asserts still exactly one probe set (the shipped happy-path test kept the walk well under the TTL and so never exercised this). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>