ZhuLinsen/daily_stock_analysis · warning · TimeoutError
题材新闻搜索等待超过调用截止时间
Error message
题材新闻搜索等待超过调用截止时间
What it means
TimeoutError from the request-coalescing wait loop in the search cache: this caller lost the race to own the cache key, waited on the owner's event, and the overall caller-supplied deadline passed before either a cached result appeared or ownership became available. Checked before waiting (remaining <= 0) — so it can fire immediately if the deadline is already exhausted.
Source
Thrown at src/search_service.py:2754
deadline: Optional[float] = None,
) -> Tuple[Optional['SearchResponse'], bool, Optional[threading.Event], bool]:
"""Return a cache hit or exclusive ownership of one cache fill.
Waiters never proceed to provider work without becoming the owner. If
an owner finishes without a cacheable response, all waiters compete for
the next reservation and the losers keep waiting on that new owner.
"""
waited = False
while True:
cached, cache_owner, cache_event = self._get_cached_or_reserve(key)
if cached is not None or cache_owner:
return cached, cache_owner, cache_event, waited
if cache_event is None: # Defensive: the reservation API promises an event here.
raise RuntimeError("搜索缓存请求合并状态异常")
waited = True
remaining = None if deadline is None else deadline - time.monotonic()
if remaining is not None and remaining <= 0:
raise TimeoutError("题材新闻搜索等待超过调用截止时间")
if remaining is None:
cached = self._wait_for_cached(key, cache_event)
else:
cached = self._wait_for_cached(key, cache_event, timeout_seconds=remaining)
if cached is not None:
return cached, False, None, waited
if deadline is not None and time.monotonic() >= deadline:
raise TimeoutError("题材新闻搜索等待超过调用截止时间")
def _put_cache(self, key: str, response: 'SearchResponse') -> None:
"""Store a successful SearchResponse in cache."""
with self._cache_lock:
# Hard cap: evict oldest entries when cache exceeds limit
_MAX_CACHE_SIZE = 500
if len(self._cache) >= _MAX_CACHE_SIZE:
now = time.time()
# First pass: remove expired entries
expired = [k for k, (ts, _) in self._cache.items() if now - ts > self._cache_ttl]View on GitHub (pinned to 5159bd72e8)
Solutions
- Increase or remove the caller's deadline so followers can wait for the in-flight result instead of timing out
- Retry the call — by then the owner has usually populated the cache and the retry returns instantly
- Warm the cache ahead of burst windows (prefetch the topic) so followers never wait
- If deadlines are strict by design, treat this as normal backpressure and degrade gracefully (skip topic news) rather than retrying
Defensive patterns
Strategy: retry
Validate before calling
def has_deadline_slack(deadline: float, min_seconds: float = 1.0) -> bool:
return deadline is None or deadline - time.monotonic() >= min_seconds Try / catch
try:
resp = search_topic_news(topic, deadline=deadline)
except TimeoutError as exc:
if "调用截止时间" in str(exc):
resp = get_cached_topic_news(topic) # usually populated by now; else degrade
else:
raise Prevention
- Set deadlines longer than the single-search timeout plus contention slack
- Prefetch hot topics before burst windows so followers hit cache, not waits
- Treat coalescing timeouts as backpressure: degrade gracefully rather than retry-loop under load
When it happens
Trigger: Two or more concurrent searches for the same topic key with a deadline: the loser blocks on the owner's cache event; if the owner takes longer than the loser's remaining deadline, the loser raises this instead of starting its own search. Also fires when a caller passes a deadline that is already in the past while the key is contended.
Common situations: Fan-out analysis requesting the same topic concurrently under tight per-request deadlines; a slow owner search stretching beyond followers' budgets; retry storms piling onto one key; deadlines computed from a shared request clock that has nearly expired.
Related errors
- 题材新闻搜索并发已满,请稍后重试
- 题材新闻搜索超过 {wait_seconds:g}s,已终止请求进程
- 题材新闻搜索超时必须大于 0 秒
- {task_name} ranking fetch timeout after {timeout_value:g}s
- {task_name} ranking fetch cooling down after previous timeou
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/d84e6d44180d37ab.
Report an issue: GitHub.