library: recompute artist aggregates at the sync boundary

Replace the per-credit invalidation with one signal from the sync's write
transactions: if artists, albums or songs changed, the source is marked stale
and a single aggregate pass refreshes counts, plays and the credited flag in
one query. Identical syncs no longer run any of those scans, and a new credit
path cannot forget to invalidate the cache.

A changed sync still reads the credit unions once instead of separately for
counts and plays, and migration 33->34 remains a frozen snapshot rather than
sharing the runtime query text.
This commit is contained in:
austinried
2026-10-19 01:32:15 +00:00
parent d908b5b26d
commit ce33a2b6fd
4 changed files with 57 additions and 122 deletions
@@ -189,8 +189,7 @@ class LibraryDaoTest {
),
)
dao.recomputeAlbumPlayData(sourceId)
dao.recomputeArtistPlayData(sourceId)
dao.recomputeAggregates(sourceId)
assertEquals(3L, dao.albumOnce(sourceId, "al-1")!!.playCount)
assertEquals(100L, dao.albumOnce(sourceId, "al-1")!!.played)
@@ -134,7 +134,7 @@ class QueryTimingMeasurementTest {
timed("home recently starred") { dao.recentlyStarredSongs(1, 20).first() }
timed("home genres") { dao.genresByMostPlayed(1).first() }
timed("home decades") { dao.decades(1).first() }
timed("recompute play data") { dao.recomputePlayData(1) }
timed("recompute aggregates") { dao.recomputeAggregates(1) }
}
private suspend fun <T : Any> PagingSource<Int, T>.firstPage(): List<T> =