Files
music_library/backlog/tasks/ml-169.8 - Fix-StatsLive.Index-10-sync-queries-blocking-TTFB.md
T
2026-05-19 12:50:56 +01:00

2.6 KiB
Raw Blame History

id, title, status, assignee, created_date, labels, dependencies, references, modified_files, parent_task_id, priority, ordinal
id title status assignee created_date labels dependencies references modified_files parent_task_id priority ordinal
ML-169.8 Fix: StatsLive.Index 10 sync queries blocking TTFB To Do
2026-05-19 11:48
perf
fix
backlog/documents/doc-27 - Phase-4-Performance-Audit-Low-Hanging-Fruit-Findings.md
lib/music_library_web/live/stats_live/index.ex
ML-169 high 29000

Description

Fix performance issue where the StatsLive.Index mount callback executes 10 synchronous Ecto queries before returning, blocking the initial render (TTFB) on the home page.

Problem

The mount callback executes 10 synchronous queries before returning {:ok, socket}:

  1. Collection.get_latest_record() — ORDER BY purchased_at LIMIT 1
  2. Collection.count_records_by_artist(limit: 20) — json_each + GROUP BY
  3. Collection.count_records_by_genre(limit: 20) — json_each + GROUP BY + subquery exclusion
  4. Collection.count_records_by_release_year(limit: 20) — substr + GROUP BY
  5. Collection.get_records_on_this_day(date) — strftime + ORDER BY
  6. Collection.count_records_by_format() — GROUP BY format
  7. Collection.count_records_by_type() — GROUP BY type
  8. Wishlist.count() — FTS search count
  9. ListeningStats.recent_activity(timezone) — 4 correlated subqueries × 100 rows
  10. ListeningStats.scrobble_count() — simple COUNT

Impact

10 blocking queries on the home page is the worst TTFB bottleneck in the app. The assign_async pattern is already in use for TopAlbums/TopArtists on the same page — apply it consistently.

Files

  • lib/music_library_web/live/stats_live/index.ex:67-109 — mount callback

Fix

Use assign_async to defer non-critical queries. The simple ones (collection_count, wishlist_count, scrobble_count for the counter badges) can stay synchronous since they're fast. Move the heavy ones to assign_async:

  • count_records_by_artist / count_records_by_genre / count_records_by_release_year
  • get_records_on_this_day
  • recent_activity (the most expensive — 4 correlated subqueries)
  • get_latest_record

Show skeleton/loading placeholders while async data loads.

Acceptance Criteria

  • #1 StatsLive.Index mount completes in under 200ms (sync queries only)
  • #2 Charts and activity feed load with loading skeleton placeholders
  • #3 Counter badges (collection, wishlist, scrobbles) show immediately
  • #4 No visual flash of empty data containers
  • #5 Assigns that are now async handle nil/failure states gracefully