store artwork seeds without read-time writes and prune periodically

This commit is contained in:
austinried
2026-10-04 00:29:58 +00:00
parent a97a5e0687
commit 11c0276929
6 changed files with 904 additions and 25 deletions
@@ -13,15 +13,9 @@ interface ArtworkSeedDao {
@Upsert
suspend fun upsert(seed: ArtworkSeed)
@Query("UPDATE artwork_seeds SET lastUsed = :lastUsed WHERE cacheKey = :cacheKey")
suspend fun touch(
cacheKey: String,
lastUsed: Long,
)
@Query(
"DELETE FROM artwork_seeds WHERE cacheKey NOT IN " +
"(SELECT cacheKey FROM artwork_seeds ORDER BY lastUsed DESC LIMIT :keep)",
"(SELECT cacheKey FROM artwork_seeds ORDER BY storedAt DESC LIMIT :keep)",
)
suspend fun prune(keep: Int)
}
@@ -119,7 +119,7 @@ val MIGRATION_12_13 =
connection.execSQL(
"CREATE TABLE IF NOT EXISTS `artwork_seeds` (`cacheKey` TEXT NOT NULL, " +
"`primary` INTEGER NOT NULL, `secondary` INTEGER, `nameBusy` INTEGER, " +
"`sourceId` INTEGER NOT NULL, `lastUsed` INTEGER NOT NULL DEFAULT 0, " +
"`sourceId` INTEGER NOT NULL, `storedAt` INTEGER NOT NULL DEFAULT 0, " +
"PRIMARY KEY(`cacheKey`), " +
"FOREIGN KEY(`sourceId`) REFERENCES `sources`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
)
@@ -255,7 +255,7 @@ data class ArtworkSeed(
val secondary: Int?,
val nameBusy: Boolean? = null,
val sourceId: Long = 0,
@ColumnInfo(defaultValue = "0") val lastUsed: Long = 0,
@ColumnInfo(defaultValue = "0") val storedAt: Long = 0,
)
class QueueKindConverter {
@@ -2,8 +2,10 @@ package com.subtracks.data.repo
import com.subtracks.data.db.SubtracksDatabase
import com.subtracks.data.model.ArtworkSeed
import java.util.concurrent.atomic.AtomicInteger
private const val MAX_STORED_SEEDS = 1024
private const val MAX_STORED_SEEDS = 10_240
private const val PRUNE_EVERY_SAVES = 128
interface ArtworkSeedStore {
suspend fun seed(cacheKey: String): ArtworkSeed?
@@ -14,18 +16,19 @@ interface ArtworkSeedStore {
class ArtworkSeedRepository(
private val db: SubtracksDatabase,
private val maxStoredSeeds: Int = MAX_STORED_SEEDS,
private val pruneEverySaves: Int = PRUNE_EVERY_SAVES,
) : ArtworkSeedStore {
override suspend fun seed(cacheKey: String): ArtworkSeed? {
val dao = db.artworkSeedDao()
val row = dao.seed(cacheKey) ?: return null
dao.touch(cacheKey, System.currentTimeMillis())
return row
}
private val savesSincePrune = AtomicInteger(0)
override suspend fun seed(cacheKey: String): ArtworkSeed? = db.artworkSeedDao().seed(cacheKey)
override suspend fun save(seed: ArtworkSeed) {
val sourceId = seed.cacheKey.substringBefore(':').toLongOrNull() ?: return
val dao = db.artworkSeedDao()
dao.upsert(seed.copy(sourceId = sourceId, lastUsed = System.currentTimeMillis()))
dao.prune(maxStoredSeeds)
dao.upsert(seed.copy(sourceId = sourceId, storedAt = System.currentTimeMillis()))
if (savesSincePrune.incrementAndGet() >= pruneEverySaves) {
savesSincePrune.set(0)
dao.prune(maxStoredSeeds)
}
}
}