Skip to content

Synchronous yfinance calls block the async event loop in market data endpoints #53

Description

@aadityat23

Problem

backend/app/market.py's _get_ticker_fast (and the equivalent info-fetching function) call yfinance's synchronous, network-blocking API directly. These functions are plain def, not async def, and are invoked directly — without asyncio.to_thread or run_in_executor — from async def route handlers in main.py such as /market/quotes and /market/indices.

Why this matters

FastAPI's async event loop is single-threaded for coroutine execution. A blocking network call inside an async def handler stalls the entire event loop for every other concurrent request — not just the one making the yfinance call — until that call returns or times out. Under real traffic (multiple users hitting the market/watchlist pages, or the periodic WebSocket refresh), this turns an occasional slow yfinance response into a global slowdown for the whole API, including unrelated /verify and /v1/fcg/verify requests.

Acceptance Criteria

  • All synchronous yfinance calls in market.py are offloaded via asyncio.to_thread (or an equivalent bounded executor pattern) when called from async route handlers

  • Offloaded provider calls use bounded concurrency (e.g. asyncio.Semaphore or an equivalent mechanism) to prevent unbounded parallel yfinance requests under load

  • A route/service-level timeout is enforced around the offloaded provider call so slow or hung upstream requests cannot indefinitely occupy worker resources

  • No change to the existing cache/rate-limit semantics (10-second TTL, stale-fallback behavior)

  • Blocking provider functions remain synchronous, while async callers use a dedicated async wrapper/adapter that owns the offload, timeout, and concurrency control

  • A test or benchmark demonstrates that a slow/blocked yfinance call no longer prevents a concurrent /health or /verify request from completing promptly

  • Tests added where appropriate

  • Documentation updated if necessary — a short comment explaining why the async offload exists, so it isn't accidentally reverted

  • (Optional) Metrics/logging distinguish cache hits, stale-cache hits, provider successes, provider timeouts, and provider errors

Files likely to modify

finverify-terminal/backend/app/market.py
finverify-terminal/backend/app/main.py

Skills required

Python, asyncio, FastAPI, concurrency concepts

Estimated difficulty

3/5

Estimated effort

4–6 hours

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions