Skip to content

fix(feed): stop a dead follow from stalling feedQuery for minutes - #436

Merged
MudDev merged 1 commit into
mainfrom
fix/feed-query-stall
Aug 27, 2026
Merged

fix(feed): stop a dead follow from stalling feedQuery for minutes#436
MudDev merged 1 commit into
mainfrom
fix/feed-query-stall

Conversation

@MudDev

@MudDev MudDev commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Problem

The dashboard's middle feed panel (Latest/Post/Comment/Topic/Mention) took minutes to load while the left menu was instant. Measured over the dashboard's websocket: a single feedQuery call took 60.2s.

Root cause chain:

  1. A stored follow's feed SQL referenced a table (message) that the followed xite's database no longer has — the follow's SQL snapshot outlived the xite's schema.
  2. db_query classifies no such table / no such column as transient and retries for the full 60-second budget at 200ms intervals. That classification exists for the rebuild window (tables dropped and recreated under a stable handle), but on a settled database the name will never appear — so every call burned the entire budget (~300 retries of log spam per call).
  3. feedQuery ran each followed xite's queries serially, so the one dead query blocked every other xite's rows behind it, and each dashboard tab interaction re-paid the stall.

Fix

A — fail fast on permanently missing schema (state.rs)

  • New is_missing_schema_error() and db_rebuild_or_clone_in_flight() (the same busy signal await_db_quiet polls: is_cloning + db_rebuilds_in_flight).
  • settle_stable_db_result now retries a missing table/column only while a clone/rebuild is actually redoing the xite's schema; on a quiet database it answers with the error immediately. Race-safe: a finished rebuild bumps db_generation, so a query that raced one gets a changed receipt and lands in the rerun branch, never this fail-fast path. Contention errors (locked/busy) retry exactly as before.

B — concurrent feed queries with a per-query deadline (command.rs)

  • feedQuery collects all followed feeds' SQL first, then runs every query concurrently (join_all), each under its own 10s tokio::time::timeout. A timed-out or failed query is skipped, so one sick xite can never serialize-stall the whole merged view.

Verification

  • cargo check -p epix-ui clean; 28 related tests pass, including feed_query_aggregates_followed_xites (exercises the rewritten handler) and the db_query receipt tests.
  • Live node with the dead follow present: feedQuery 60.2s → 0.36s cold / 0.01s warm, identical rows returned. Log now shows a single schema settled, not retrying line per call instead of ~300 retry lines.
  • Boot-churn behavior: within the first seconds after startup, a feed query racing initial DB builds can hit the 10s deadline (rows from that xite skipped for that call) and settles immediately after — worst case is now 10s once, not minutes per call.

The dashboard's merged feed took minutes to load whenever one followed
xite's saved feed SQL referenced a table its database no longer has (a
follow whose SQL outlived the xite's schema). Two compounding causes:

- db_query classified "no such table"/"no such column" as transient and
  retried them for the full 60s budget. That classification exists for
  the rebuild window where tables are dropped and recreated under a
  stable handle - but on a settled database the name never appears, so
  every feedQuery call burned the whole budget on the dead query.

- feedQuery ran each followed xite's queries serially, so that one 60s
  stall blocked every other xite's rows behind it.

Fix both ends:

- settle_stable_db_result now retries a missing table/column only while
  a clone or db rebuild is actually in flight for the xite (the same
  busy signal await_db_quiet polls); on a quiet database it answers with
  the error immediately. This is race-safe: a finished rebuild bumps
  db_generation, so a query that raced one lands in the receipt-changed
  retry branch, never this fail-fast path. SQLite contention errors
  ("locked"/"busy") retry exactly as before.

- feedQuery runs all followed feeds' queries concurrently, each under
  its own 10s deadline; a timed-out or failed query is skipped instead
  of stalling the merged view.

Measured on a live node with a dead "New conversations" follow on a
xite missing its `message` table: feedQuery 60.2s -> 0.36s cold /
0.01s warm, identical rows.
@sonarqubecloud

Copy link
Copy Markdown

@MudDev
MudDev merged commit 37ce24e into main Aug 27, 2026
10 of 11 checks passed
@MudDev
MudDev deleted the fix/feed-query-stall branch August 27, 2026 03:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant