sync: batch playlist origin lookups

This commit is contained in:
austinried
2026-11-14 12:05:29 +00:00
parent 746446be86
commit 50c4dfce03
2 changed files with 61 additions and 9 deletions
@@ -219,6 +219,7 @@ class SyncService(
} }
} }
connection.upsertChanged("song_artists", songArtistKey, artists) { it.columns() } connection.upsertChanged("song_artists", songArtistKey, artists) { it.columns() }
connection.recordPlaylistOrigins(batch.filter { previousStates[it.id] == null }.map { it.id })
batch.forEach { song -> batch.forEach { song ->
val previous = previousStates[song.id] val previous = previousStates[song.id]
val next = nextStates.getValue(song.id) val next = nextStates.getValue(song.id)
@@ -242,7 +243,9 @@ class SyncService(
ids.forEach { changedOrigins += origin(QueueKind.Song, it) } ids.forEach { changedOrigins += origin(QueueKind.Song, it) }
db.useWriterConnection { connection -> db.useWriterConnection { connection ->
connection.immediateTransaction { connection.immediateTransaction {
connection.storedSongSyncStates(ids.toList()).values.forEach { recordSongOrigins(it, null) } val previousStates = connection.storedSongSyncStates(ids.toList())
connection.recordPlaylistOrigins(previousStates.keys)
previousStates.values.forEach { recordSongOrigins(it, null) }
if (connection.completedDownloadIds(ids.toList()).isNotEmpty()) changedOrigins += origin(QueueKind.Downloaded, "") if (connection.completedDownloadIds(ids.toList()).isNotEmpty()) changedOrigins += origin(QueueKind.Downloaded, "")
db.libraryDao().deleteSongs(source.id, ids) db.libraryDao().deleteSongs(source.id, ids)
connection.persistPendingOrigins() connection.persistPendingOrigins()
@@ -250,18 +253,19 @@ class SyncService(
} }
} }
private suspend fun PooledConnection.recordPlaylistOrigins(songIds: Collection<String>) {
readSourceIds(
songIds,
{ "SELECT playlistId FROM playlist_songs WHERE sourceId = ? AND songId IN ($it)" },
) { statement ->
changedOrigins += origin(QueueKind.Playlist, statement.getText(0))
}
}
private suspend fun PooledConnection.recordSongOrigins( private suspend fun PooledConnection.recordSongOrigins(
previous: SongSyncState?, previous: SongSyncState?,
next: SongSyncState?, next: SongSyncState?,
) { ) {
if ((previous == null) != (next == null)) {
val songId = (previous ?: next)!!.id
usePrepared("SELECT playlistId FROM playlist_songs WHERE sourceId = ? AND songId = ?") { statement ->
statement.bindLong(1, source.id)
statement.bindText(2, songId)
while (statement.step()) changedOrigins += origin(QueueKind.Playlist, statement.getText(0))
}
}
val states = listOfNotNull(previous, next) val states = listOfNotNull(previous, next)
states.mapTo(changedOrigins) { origin(QueueKind.Song, it.id) } states.mapTo(changedOrigins) { origin(QueueKind.Song, it.id) }
states.mapNotNullTo(changedOrigins) { it.albumId?.let { id -> origin(QueueKind.Album, id) } } states.mapNotNullTo(changedOrigins) { it.albumId?.let { id -> origin(QueueKind.Album, id) } }
@@ -2034,6 +2034,54 @@ class SyncServiceTest {
assertTrue(restored.changedOrigins.contains(QueueOrigin(QueueKind.Playlist, 1, "p1"))) assertTrue(restored.changedOrigins.contains(QueueOrigin(QueueKind.Playlist, 1, "p1")))
} }
@Test
fun songPresenceChangesBatchPlaylistMembershipQueries() =
runTest {
val executions = Collections.synchronizedList(mutableListOf<SqlExecution>())
val loggingDb = createTestDatabase(LoggingSQLiteDriver(BundledSQLiteDriver()) { executions += it })
try {
loggingDb.sourcesDao().upsertSource(
Source(id = 1, name = "test", address = "http://localhost", isActive = true, createdAt = 0),
)
val songs = listOf(song("s1"), song("s2"), song("s3"))
val source =
FakeMusicSource(
songs = songs,
playlists = listOf(playlist("p1")),
playlistSongs = songs.mapIndexed { index, song -> PlaylistSong(1, "p1", song.id, index.toLong()) },
)
fun membershipQueryCount() =
synchronized(executions) {
executions.count {
it.sql.startsWith("SELECT playlistId FROM playlist_songs WHERE sourceId = ? AND songId")
}
}
SyncService(loggingDb, source).sync()
val initialQueries = membershipQueryCount()
loggingDb.queueDao().clearPendingSyncOrigins(1)
synchronized(executions) { executions.clear() }
source.songs = emptyList()
val deleted = SyncService(loggingDb, source).sync()
val deletionQueries = membershipQueryCount()
assertTrue(deleted.changedOrigins.contains(QueueOrigin(QueueKind.Playlist, 1, "p1")))
assertTrue(loggingDb.queueDao().pendingSyncOrigins(1).any { it.kind == QueueKind.Playlist && it.refId == "p1" })
loggingDb.queueDao().clearPendingSyncOrigins(1)
synchronized(executions) { executions.clear() }
source.songs = songs
val restored = SyncService(loggingDb, source).sync()
val restorationQueries = membershipQueryCount()
assertTrue(restored.changedOrigins.contains(QueueOrigin(QueueKind.Playlist, 1, "p1")))
assertTrue(loggingDb.queueDao().pendingSyncOrigins(1).any { it.kind == QueueKind.Playlist && it.refId == "p1" })
assertEquals(listOf(1, 1, 1), listOf(initialQueries, deletionQueries, restorationQueries))
} finally {
loggingDb.close()
}
}
@Test @Test
fun downloadedSongSortChangesReportDownloadedOrigins() = fun downloadedSongSortChangesReportDownloadedOrigins() =
runTest { runTest {