Effort: thorough
mv_skill_usage_daily (added in q-skills-usage-telemetry) proved
materialized views are the right tool for read-heavy aggregations
on SciDEX's PG. But the homepage strip, the top-hypotheses
leaderboard, the senate quality dashboard, and the "Active markets"
strip each currently rebuild from scratch on every request — each
joining 4-7 tables, sorting, and limiting to 10-50 rows. Add five
selective materialized views with concurrent refresh, one per
high-traffic dashboard widget, and route the API handlers to read
from them.
migrations/<date>_dashboard_mat_views.sql:mv_top_hypotheses_by_score(hypothesis_id, title,
composite_score, rank, last_updated) — top 50 bymv_active_markets_summary(market_id, hypothesis_id,
current_price, volume_24h, position_count, last_trade_at)lifecycle_state='active'; refresh 60 s.mv_recent_artifacts(artifact_id, kind, title, created_at,
contributor_count) — 100 most recent across kinds;mv_homepage_stats(metric_name, metric_value, computed_at)mv_quality_dashboard_rollup(layer, gate, total, passing,
ratio, last_run) — rolls up quality_gates results;UNIQUE index on its natural key soREFRESH MATERIALIZED VIEW CONCURRENTLY is safe.
scidex-mv-refresh.timer (every 30 s) drives a Python entryscidex/senate/mv_refresh.py which dispatches each MVmat_view_schedules table). Skips refresh if a previous oneapi_routes/senate.py (homepage stats), api_routes/agora.pyapi_routes/forge.py (active markets)?fresh=1 debug flag.
X-Data-Staleness-Seconds header sourced fromlast_updated. A small footer note ("Updated 22 s ago")tests/test_dashboard_mat_views.py: synthetic/ and/senate/quality-dashboard before/after; expect ≥5x latency?fresh=1 flag is the rollback hatch; remove it afterq-skills-usage-telemetry — pattern this follows.q-perf-hot-path-optimizer — picks the right widgets.q-perf-cdn-friendly-views — MV-backed routes are nowFiles created:
migrations/20260428_dashboard_mat_views.sql — five MVs + mat_view_schedules table with 5 seed rowsscidex/senate/mv_refresh.py — dispatcher: reads schedules, skips is_running views, falls back to non-CONCURRENT for initial population, error-safe transaction handlingdeploy/bootstrap/systemd/scidex-mv-refresh.{service,timer} — 30 s systemd timer driving the dispatchertests/test_dashboard_mat_views.py — 16 tests covering schema, refresh, dispatch logic, concurrent-read non-blocking, and stalenessapi_routes/agora.py — GET /api/agora/top-hypotheses (MV-backed, ?fresh=1 fallback, X-Data-Staleness-Seconds header)api_routes/forge.py — GET /api/forge/active-markets (MV-backed)api_routes/senate.py — GET /api/senate/homepage-stats and GET /api/senate/quality-rollup (both MV-backed)prediction_markets uses status (not lifecycle_state): adapted to status IN ('active','open')quality_gates table — used artifact_gate_results (discovered via quality_dashboard.py inspection)artifacts.contributors is JSONB array — used jsonb_array_length for contributor_countpg_matviews.ispopulated and falls back to non-concurrent on first runLatency evidence (p95, 20-run warm DB):