mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-15 00:42:54 +01:00
library: keep artist queries index-driven on large libraries
Rewrite artist queue membership as a UNION of indexed song-id branches instead of an OR that scanned every song, and recompute artist play data from an explicit credited-album set instead of scanning all albums per artist. Run PRAGMA optimize after syncing songs and before play aggregation, and derive downloaded artist lists and filters from downloads rather than all songs. Count downloaded albums per artist with artist-first branches. Extend the query timing harness with the artist playback, download and status queries plus a partial-download fixture.
This commit is contained in:
@@ -3,6 +3,8 @@ package com.subtracks.data.db
|
||||
import android.content.Context
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room3.Room
|
||||
import androidx.room3.immediateTransaction
|
||||
import androidx.room3.useWriterConnection
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
@@ -61,6 +63,27 @@ class QueryTimingMeasurementTest {
|
||||
val secondSyncMs = wall { SyncService(db, source).sync() }
|
||||
val secondSyncExecutions = executions.toList()
|
||||
|
||||
db.useWriterConnection { connection ->
|
||||
connection.immediateTransaction {
|
||||
connection.usePrepared(
|
||||
"INSERT INTO song_downloads (sourceId, songId, status, downloadedAt) VALUES (?, ?, ?, ?)",
|
||||
) { statement ->
|
||||
(0 until songCount step 10).forEach { i ->
|
||||
statement.bindLong(1, 1L)
|
||||
statement.bindText(2, "s$i")
|
||||
statement.bindText(3, "Completed")
|
||||
statement.bindLong(4, i.toLong())
|
||||
statement.step()
|
||||
statement.reset()
|
||||
statement.clearBindings()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
db.useWriterConnection { connection ->
|
||||
connection.usePrepared("ANALYZE") { it.step() }
|
||||
}
|
||||
|
||||
executions.clear()
|
||||
measureReads()
|
||||
val readExecutions = executions.toList()
|
||||
@@ -93,6 +116,13 @@ class QueryTimingMeasurementTest {
|
||||
timed("artists first page (frequent)") { dao.artistsByFrequent(1, 0, "").firstPage() }
|
||||
timed("artist albums") { dao.albumsForArtist(1, "ar0").first() }
|
||||
timed("artist name") { dao.artistName(1, "ar0").first() }
|
||||
timed("artist songs") { db.queueDao().artistSongs(1, "ar0", 0, 100) }
|
||||
timed("artist length") { db.queueDao().artistLength(1, "ar0") }
|
||||
timed("artist song ids") { db.queueDao().artistSongIds(1, "ar0") }
|
||||
timed("downloaded artists") { dao.downloadedArtists(1).firstPage() }
|
||||
timed("artists downloaded filter") { dao.artistsByName(1, 0, "", downloadedFilter = 1).firstPage() }
|
||||
timed("artist statuses") { db.downloadDao().artistStatuses(1).first() }
|
||||
timed("artist status") { db.downloadDao().artistStatus(1, "ar0").first() }
|
||||
timed("playlists first page (name)") { dao.playlistsByName(1, "").firstPage() }
|
||||
timed("albums deep page") { dao.albumsByName(1, 0, "").deepPage(1000) }
|
||||
timed("albums search FTS") { dao.albumsByName(1, 0, "Album 1").firstPage() }
|
||||
|
||||
@@ -1026,7 +1026,7 @@ class SyncServiceTest {
|
||||
insertSource()
|
||||
val source =
|
||||
FakeMusicSource(
|
||||
artists = listOf(artist("a1", "A"), artist("a2", "B")),
|
||||
artists = listOf(artist("a1", "A"), artist("a2", "B"), artist("a3", "C")),
|
||||
albums =
|
||||
listOf(
|
||||
album("al1").copy(
|
||||
@@ -1054,6 +1054,15 @@ class SyncServiceTest {
|
||||
.map { it.name }
|
||||
.toSet(),
|
||||
)
|
||||
|
||||
val statuses =
|
||||
db
|
||||
.downloadDao()
|
||||
.artistStatuses(1)
|
||||
.first()
|
||||
.associateBy { it.id }
|
||||
assertEquals(setOf("a1", "a2"), statuses.keys)
|
||||
assertEquals(1L, statuses.getValue("a2").downloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user