Skip to content
This repository has been archived by the owner on Nov 12, 2024. It is now read-only.

Move database operations to specific dispatchers #1400

Merged
merged 1 commit into from
Jul 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import kotlinx.coroutines.CoroutineDispatcher

data class AppCoroutineDispatchers(
val io: CoroutineDispatcher,
val databaseWrite: CoroutineDispatcher,
val databaseRead: CoroutineDispatcher,
val computation: CoroutineDispatcher,
val main: CoroutineDispatcher,
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ actual interface SqlDelightDatabasePlatformComponent {
fun provideDriverFactory(configuration: DatabaseConfiguration): SqlDriver {
return when {
configuration.inMemory -> inMemoryDriver(Database.Schema)
else -> NativeSqliteDriver(schema = Database.Schema, name = "tivi.db")
else -> {
NativeSqliteDriver(
schema = Database.Schema,
name = "tivi.db",
maxReaderConnections = 4,
)
}
}.also { driver ->
driver.execute(null, "PRAGMA foreign_keys=ON", 0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

package app.tivi.data.util

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import org.mobilenativefoundation.store.store5.Fetcher
import org.mobilenativefoundation.store.store5.SourceOfTruth
import org.mobilenativefoundation.store.store5.Store
Expand All @@ -31,3 +34,25 @@ inline fun <Key : Any, Model : Any> storeBuilder(
fetcher: Fetcher<Key, Model>,
sourceOfTruth: SourceOfTruth<Key, Model>,
): StoreBuilder<Key, Model> = StoreBuilder.from(fetcher, sourceOfTruth)

fun <Key : Any, Local : Any> SourceOfTruth<Key, Local>.usingDispatchers(
readDispatcher: CoroutineDispatcher,
writeDispatcher: CoroutineDispatcher,
): SourceOfTruth<Key, Local> {
val wrapped = this
return object : SourceOfTruth<Key, Local> {
override fun reader(key: Key): Flow<Local?> = wrapped.reader(key).flowOn(readDispatcher)

override suspend fun write(key: Key, value: Local) = withContext(writeDispatcher) {
wrapped.write(key, value)
}

override suspend fun delete(key: Key) = withContext(writeDispatcher) {
wrapped.delete(key)
}

override suspend fun deleteAll() = withContext(writeDispatcher) {
wrapped.deleteAll()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import app.tivi.data.daos.updatePage
import app.tivi.data.db.DatabaseTransactionRunner
import app.tivi.data.models.PopularShowEntry
import app.tivi.data.util.storeBuilder
import app.tivi.data.util.usingDispatchers
import app.tivi.inject.ApplicationScope
import app.tivi.util.AppCoroutineDispatchers
import kotlin.time.Duration.Companion.hours
import me.tatarka.inject.annotations.Inject
import org.mobilenativefoundation.store.store5.Fetcher
Expand All @@ -26,6 +28,7 @@ class PopularShowsStore(
showDao: TiviShowDao,
lastRequestStore: PopularShowsLastRequestStore,
transactionRunner: DatabaseTransactionRunner,
dispatchers: AppCoroutineDispatchers,
) : Store<Int, List<PopularShowEntry>> by storeBuilder(
fetcher = Fetcher.of { page: Int ->
dataSource(page, 20).let { response ->
Expand Down Expand Up @@ -53,7 +56,10 @@ class PopularShowsStore(
}
},
delete = popularShowsDao::deletePage,
deleteAll = popularShowsDao::deleteAll,
deleteAll = { transactionRunner(popularShowsDao::deleteAll) },
).usingDispatchers(
readDispatcher = dispatchers.databaseRead,
writeDispatcher = dispatchers.databaseWrite,
),
).validator(
Validator.by { lastRequestStore.isRequestValid(3.hours) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import app.tivi.data.daos.updatePage
import app.tivi.data.db.DatabaseTransactionRunner
import app.tivi.data.models.RecommendedShowEntry
import app.tivi.data.util.storeBuilder
import app.tivi.data.util.usingDispatchers
import app.tivi.inject.ApplicationScope
import app.tivi.util.AppCoroutineDispatchers
import kotlin.time.Duration.Companion.days
import me.tatarka.inject.annotations.Inject
import org.mobilenativefoundation.store.store5.Fetcher
Expand All @@ -26,6 +28,7 @@ class RecommendedShowsStore(
showDao: TiviShowDao,
lastRequestStore: RecommendedShowsLastRequestStore,
transactionRunner: DatabaseTransactionRunner,
dispatchers: AppCoroutineDispatchers,
) : Store<Int, List<RecommendedShowEntry>> by storeBuilder(
fetcher = Fetcher.of { page: Int ->
dataSource(page, 20).let { response ->
Expand Down Expand Up @@ -56,7 +59,10 @@ class RecommendedShowsStore(
}
},
delete = recommendedDao::deletePage,
deleteAll = recommendedDao::deleteAll,
deleteAll = { transactionRunner(recommendedDao::deleteAll) },
).usingDispatchers(
readDispatcher = dispatchers.databaseRead,
writeDispatcher = dispatchers.databaseWrite,
),
).validator(
Validator.by { lastRequestStore.isRequestValid(3.days) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import app.tivi.data.daos.getIdOrSavePlaceholder
import app.tivi.data.db.DatabaseTransactionRunner
import app.tivi.data.models.RelatedShowEntry
import app.tivi.data.util.storeBuilder
import app.tivi.data.util.usingDispatchers
import app.tivi.inject.ApplicationScope
import app.tivi.util.AppCoroutineDispatchers
import kotlin.time.Duration.Companion.days
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import me.tatarka.inject.annotations.Inject
import org.mobilenativefoundation.store.store5.Fetcher
import org.mobilenativefoundation.store.store5.SourceOfTruth
Expand All @@ -27,6 +30,7 @@ class RelatedShowsStore(
showDao: TiviShowDao,
lastRequestStore: RelatedShowsLastRequestStore,
transactionRunner: DatabaseTransactionRunner,
dispatchers: AppCoroutineDispatchers,
) : Store<Long, RelatedShows> by storeBuilder(
fetcher = Fetcher.of { showId: Long ->
runCatching { tmdbDataSource(showId) }
Expand All @@ -38,14 +42,16 @@ class RelatedShowsStore(
}
.getOrThrow()
.let { result ->
transactionRunner {
lastRequestStore.updateLastRequest(showId)
withContext(dispatchers.databaseWrite) {
transactionRunner {
lastRequestStore.updateLastRequest(showId)

result.map { (show, entry) ->
entry.copy(
showId = showId,
otherShowId = showDao.getIdOrSavePlaceholder(show),
)
result.map { (show, entry) ->
entry.copy(
showId = showId,
otherShowId = showDao.getIdOrSavePlaceholder(show),
)
}
}
}
}
Expand All @@ -63,7 +69,10 @@ class RelatedShowsStore(
}
},
delete = relatedShowsDao::deleteWithShowId,
deleteAll = relatedShowsDao::deleteAll,
deleteAll = { transactionRunner(relatedShowsDao::deleteAll) },
).usingDispatchers(
readDispatcher = dispatchers.databaseRead,
writeDispatcher = dispatchers.databaseWrite,
),
).validator(
Validator.by { lastRequestStore.isRequestValid(it.showId, 28.days) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import app.tivi.data.daos.saveImagesIfEmpty
import app.tivi.data.db.DatabaseTransactionRunner
import app.tivi.data.models.TiviShow
import app.tivi.inject.ApplicationScope
import app.tivi.util.AppCoroutineDispatchers
import kotlinx.coroutines.withContext
import me.tatarka.inject.annotations.Inject

@ApplicationScope
Expand All @@ -20,6 +22,7 @@ class SearchRepository(
private val showDao: TiviShowDao,
private val tmdbDataSource: SearchDataSource,
private val transactionRunner: DatabaseTransactionRunner,
private val dispatchers: AppCoroutineDispatchers,
) {
private val cache by lazy { LruCache<String, List<Long>>(20) }

Expand Down Expand Up @@ -49,15 +52,17 @@ class SearchRepository(
private suspend fun fetchFromTmdb(query: String): List<Long> {
return tmdbDataSource.search(query)
.map { (show, images) ->
transactionRunner {
val showId = showDao.getIdOrSavePlaceholder(show)
if (images.isNotEmpty()) {
showTmdbImagesDao.saveImagesIfEmpty(
showId = showId,
images = images.map { it.copy(showId = showId) },
)
withContext(dispatchers.databaseWrite) {
transactionRunner {
val showId = showDao.getIdOrSavePlaceholder(show)
if (images.isNotEmpty()) {
showTmdbImagesDao.saveImagesIfEmpty(
showId = showId,
images = images.map { it.copy(showId = showId) },
)
}
showId
}
showId
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import app.tivi.data.daos.saveImages
import app.tivi.data.db.DatabaseTransactionRunner
import app.tivi.data.models.ShowImages
import app.tivi.data.util.storeBuilder
import app.tivi.data.util.usingDispatchers
import app.tivi.inject.ApplicationScope
import app.tivi.util.AppCoroutineDispatchers
import kotlin.time.Duration.Companion.days
import kotlinx.coroutines.flow.map
import me.tatarka.inject.annotations.Inject
Expand All @@ -26,6 +28,7 @@ class ShowImagesStore(
lastRequestStore: ShowImagesLastRequestStore,
dataSource: ShowImagesDataSource,
transactionRunner: DatabaseTransactionRunner,
dispatchers: AppCoroutineDispatchers,
) : Store<Long, ShowImages> by storeBuilder(
fetcher = Fetcher.of { showId: Long ->
val show = showDao.getShowWithId(showId)
Expand All @@ -48,7 +51,10 @@ class ShowImagesStore(
}
},
delete = showTmdbImagesDao::deleteForShowId,
deleteAll = showTmdbImagesDao::deleteAll,
deleteAll = { transactionRunner(showTmdbImagesDao::deleteAll) },
).usingDispatchers(
readDispatcher = dispatchers.databaseRead,
writeDispatcher = dispatchers.databaseWrite,
),
).validator(
Validator.by { lastRequestStore.isRequestValid(it.showId, 180.days) },
Expand Down
13 changes: 11 additions & 2 deletions data/shows/src/commonMain/kotlin/app/tivi/data/shows/ShowStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import app.tivi.data.db.DatabaseTransactionRunner
import app.tivi.data.models.TiviShow
import app.tivi.data.util.mergeShows
import app.tivi.data.util.storeBuilder
import app.tivi.data.util.usingDispatchers
import app.tivi.inject.ApplicationScope
import app.tivi.util.AppCoroutineDispatchers
import kotlin.time.Duration.Companion.days
import kotlinx.coroutines.withContext
import me.tatarka.inject.annotations.Inject
import org.mobilenativefoundation.store.store5.Fetcher
import org.mobilenativefoundation.store.store5.SourceOfTruth
Expand All @@ -25,9 +28,12 @@ class ShowStore(
traktDataSource: TraktShowDataSource,
tmdbDataSource: TmdbShowDataSource,
transactionRunner: DatabaseTransactionRunner,
dispatchers: AppCoroutineDispatchers,
) : Store<Long, TiviShow> by storeBuilder(
fetcher = Fetcher.of { id: Long ->
val savedShow = showDao.getShowWithIdOrThrow(id)
val savedShow = withContext(dispatchers.databaseWrite) {
showDao.getShowWithIdOrThrow(id)
}

val traktResult = runCatching { traktDataSource.getShow(savedShow) }
if (traktResult.isSuccess) {
Expand All @@ -54,7 +60,10 @@ class ShowStore(
}
},
delete = showDao::delete,
deleteAll = showDao::deleteAll,
deleteAll = { transactionRunner(showDao::deleteAll) },
).usingDispatchers(
readDispatcher = dispatchers.databaseRead,
writeDispatcher = dispatchers.databaseWrite,
),
).validator(
Validator.by { lastRequestStore.isRequestValid(it.id, 14.days) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ abstract class TestDataSourceComponent :
val testDispatcher = UnconfinedTestDispatcher()
return AppCoroutineDispatchers(
io = testDispatcher,
databaseRead = testDispatcher,
databaseWrite = testDispatcher,
computation = testDispatcher,
main = testDispatcher,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import app.tivi.data.daos.updatePage
import app.tivi.data.db.DatabaseTransactionRunner
import app.tivi.data.models.TrendingShowEntry
import app.tivi.data.util.storeBuilder
import app.tivi.data.util.usingDispatchers
import app.tivi.inject.ApplicationScope
import app.tivi.util.AppCoroutineDispatchers
import kotlin.time.Duration.Companion.hours
import kotlinx.coroutines.withContext
import me.tatarka.inject.annotations.Inject
import org.mobilenativefoundation.store.store5.Fetcher
import org.mobilenativefoundation.store.store5.SourceOfTruth
Expand All @@ -26,15 +29,18 @@ class TrendingShowsStore(
showDao: TiviShowDao,
lastRequestStore: TrendingShowsLastRequestStore,
transactionRunner: DatabaseTransactionRunner,
dispatchers: AppCoroutineDispatchers,
) : Store<Int, List<TrendingShowEntry>> by storeBuilder(
fetcher = Fetcher.of { page: Int ->
dataSource(page, 20).let { response ->
transactionRunner {
if (page == 0) {
lastRequestStore.updateLastRequest()
}
response.map { (show, entry) ->
entry.copy(showId = showDao.getIdOrSavePlaceholder(show), page = page)
withContext(dispatchers.databaseWrite) {
transactionRunner {
if (page == 0) {
lastRequestStore.updateLastRequest()
}
response.map { (show, entry) ->
entry.copy(showId = showDao.getIdOrSavePlaceholder(show), page = page)
}
}
}
}
Expand All @@ -53,7 +59,10 @@ class TrendingShowsStore(
}
},
delete = trendingShowsDao::deletePage,
deleteAll = trendingShowsDao::deleteAll,
deleteAll = { transactionRunner(trendingShowsDao::deleteAll) },
).usingDispatchers(
readDispatcher = dispatchers.databaseRead,
writeDispatcher = dispatchers.databaseWrite,
),
).validator(
Validator.by { lastRequestStore.isRequestValid(3.hours) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import app.tivi.data.db.DatabaseTransactionRunner
import app.tivi.data.models.WatchedShowEntry
import app.tivi.data.util.storeBuilder
import app.tivi.data.util.syncerForEntity
import app.tivi.data.util.usingDispatchers
import app.tivi.inject.ApplicationScope
import app.tivi.util.AppCoroutineDispatchers
import app.tivi.util.Logger
import kotlin.time.Duration.Companion.hours
import me.tatarka.inject.annotations.Inject
Expand All @@ -28,6 +30,7 @@ class WatchedShowsStore(
lastRequestStore: WatchedShowsLastRequestStore,
logger: Logger,
transactionRunner: DatabaseTransactionRunner,
dispatchers: AppCoroutineDispatchers,
) : Store<Unit, List<WatchedShowEntry>> by storeBuilder(
fetcher = Fetcher.of {
dataSource().let { response ->
Expand Down Expand Up @@ -60,9 +63,12 @@ class WatchedShowsStore(
},
delete = {
// Delete of an entity here means the entire list
watchedShowsDao.deleteAll()
transactionRunner(watchedShowsDao::deleteAll)
},
deleteAll = watchedShowsDao::deleteAll,
deleteAll = { transactionRunner(watchedShowsDao::deleteAll) },
).usingDispatchers(
readDispatcher = dispatchers.databaseRead,
writeDispatcher = dispatchers.databaseWrite,
),
).validator(
Validator.by { lastRequestStore.isRequestValid(6.hours) },
Expand Down
Loading