ZhuLinsen/daily_stock_analysis · warning · TimeoutError
{task_name} ranking fetch cooling down after previous timeou
Error message
{task_name} ranking fetch cooling down after previous timeout What it means
TimeoutError(f'{task_name} ranking fetch cooling down after previous timeout') is raised by MarketHotspotService._get_or_submit_ranking_fetch when an inflight key is still inside its cooldown window (retry_after > now) after a previous timeout. It is a deliberate circuit-breaker: calls made too soon after a timeout fail fast instead of stacking new requests against a slow source.
Source
Thrown at src/services/market_hotspot_service.py:467
) from exc
@classmethod
def _get_or_submit_ranking_fetch(
cls,
task: Callable[[], Any],
*,
inflight_key: Hashable,
task_name: str,
) -> Future:
submitted: Future
worker: threading.Thread
with cls._ranking_fetch_futures_lock:
retry_entry = cls._ranking_fetch_retry_after.get(inflight_key)
now = time.monotonic()
if retry_entry is not None:
retry_future, retry_after = retry_entry
if retry_after > now:
raise TimeoutError(
f"{task_name} ranking fetch cooling down after previous timeout"
)
cls._ranking_fetch_retry_after.pop(inflight_key, None)
if cls._ranking_fetch_futures.get(inflight_key) is retry_future:
cls._ranking_fetch_futures.pop(inflight_key, None)
if retry_future.done() or retry_future.cancelled():
cls._ranking_fetch_slots.release()
else:
cls._ranking_fetch_detached_futures.add(retry_future)
current = cls._ranking_fetch_futures.get(inflight_key)
if current is not None:
if not current.done():
return current
cls._ranking_fetch_futures.pop(inflight_key, None)
cls._ranking_fetch_slots.release()
if not cls._ranking_fetch_slots.acquire(blocking=False):View on GitHub (pinned to 5159bd72e8)
Solutions
- Back off and retry after the cooldown: sleep/retry with a delay >= max(timeout_value, 0.2s) before calling again
- Catch TimeoutError at the call site and serve cached/last-known ranking data during cooldown
- If cooldowns are too aggressive relative to your polling cadence, lower the configured timeout or re-evaluate the retry delay constant
- Ensure only one scheduler (not several) triggers the same ranking fetch to avoid overlapping retry storms
Example fix
# before: immediate retry hammers the circuit breaker
try:
data = fetch_ranking()
except TimeoutError:
data = fetch_ranking() # raises 'cooling down'
# after: back off past the cooldown window
try:
data = fetch_ranking()
except TimeoutError:
time.sleep(0.5)
data = fetch_ranking() Defensive patterns
Strategy: retry
Try / catch
try:
data = fetch_ranking(key)
except TimeoutError as exc:
if "cooling down" in str(exc):
time.sleep(max(0.2, configured_timeout)) # past cooldown window
data = fetch_ranking(key) Prevention
- Always back off at least max(timeout, 0.2s) after a timeout before retrying
- Poll less frequently than the cooldown window
- Serve cached data during cooldown instead of failing the request
When it happens
Trigger: Calling the same ranking fetch (same inflight_key) again within the cooldown period — max(previous timeout value, RANKING_FETCH_TIMEOUT_RETRY_DELAY_SECONDS=0.2s) — after that key previously hit 'ranking fetch timeout after ...s'. Higher configured timeouts mean longer cooldowns.
Common situations: Retry loops that immediately re-invoke after a timeout (no backoff), a frontend polling interval shorter than the cooldown, or multiple concurrent requests for the same ranking that all funnel into one timed-out key and then fail fast in quick succession.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- {task_name} ranking fetch timeout after {timeout_value:g}s
- {task_name} ranking fetch in-flight limit reached
- portfolio_busy
- 题材新闻搜索并发已满,请稍后重试
- 题材新闻搜索等待超过调用截止时间
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/04e84afdcc1fe946.
Report an issue: GitHub.