feat: separate db reads and writes to separate dispatchers

This commit is contained in:
Harsh Shandilya 2025-03-11 17:32:56 +05:30
parent f818da6f64
commit 976c9dd064
5 changed files with 38 additions and 20 deletions

View file

@ -6,7 +6,8 @@
*/ */
package dev.msfjarvis.claw.android.viewmodel package dev.msfjarvis.claw.android.viewmodel
import dev.msfjarvis.claw.core.injection.DatabaseDispatcher import dev.msfjarvis.claw.core.injection.DatabaseReadDispatcher
import dev.msfjarvis.claw.core.injection.DatabaseWriteDispatcher
import dev.msfjarvis.claw.database.local.PostComments import dev.msfjarvis.claw.database.local.PostComments
import dev.msfjarvis.claw.database.local.PostCommentsQueries import dev.msfjarvis.claw.database.local.PostCommentsQueries
import dev.msfjarvis.claw.model.Comment import dev.msfjarvis.claw.model.Comment
@ -18,14 +19,15 @@ class CommentsRepository
@Inject @Inject
constructor( constructor(
private val postCommentsQueries: PostCommentsQueries, private val postCommentsQueries: PostCommentsQueries,
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher, @DatabaseReadDispatcher private val readDispatcher: CoroutineDispatcher,
@DatabaseWriteDispatcher private val writeDispatcher: CoroutineDispatcher,
) { ) {
suspend fun getSeenComments(postId: String) = suspend fun getSeenComments(postId: String) =
withContext(dbDispatcher) { postCommentsQueries.getCommentIds(postId).executeAsOneOrNull() } withContext(readDispatcher) { postCommentsQueries.getCommentIds(postId).executeAsOneOrNull() }
suspend fun markSeenComments(postId: String, comments: List<Comment>) { suspend fun markSeenComments(postId: String, comments: List<Comment>) {
withContext(dbDispatcher) { withContext(writeDispatcher) {
postCommentsQueries.rememberComments(PostComments(postId, comments.map { it.shortId })) postCommentsQueries.rememberComments(PostComments(postId, comments.map { it.shortId }))
} }
} }

View file

@ -8,7 +8,8 @@ package dev.msfjarvis.claw.android.viewmodel
import app.cash.sqldelight.coroutines.asFlow import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList import app.cash.sqldelight.coroutines.mapToList
import dev.msfjarvis.claw.core.injection.DatabaseDispatcher import dev.msfjarvis.claw.core.injection.DatabaseReadDispatcher
import dev.msfjarvis.claw.core.injection.DatabaseWriteDispatcher
import dev.msfjarvis.claw.database.local.ReadPostsQueries import dev.msfjarvis.claw.database.local.ReadPostsQueries
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
@ -18,11 +19,12 @@ class ReadPostsRepository
@Inject @Inject
constructor( constructor(
private val readPostsQueries: ReadPostsQueries, private val readPostsQueries: ReadPostsQueries,
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher, @DatabaseReadDispatcher private val readDispatcher: CoroutineDispatcher,
@DatabaseWriteDispatcher private val writeDispatcher: CoroutineDispatcher,
) { ) {
val readPosts = readPostsQueries.selectAllPosts().asFlow().mapToList(dbDispatcher) val readPosts = readPostsQueries.selectAllPosts().asFlow().mapToList(readDispatcher)
suspend fun markRead(postId: String) { suspend fun markRead(postId: String) {
withContext(dbDispatcher) { readPostsQueries.markRead(postId) } withContext(writeDispatcher) { readPostsQueries.markRead(postId) }
} }
} }

View file

@ -8,7 +8,8 @@ package dev.msfjarvis.claw.android.viewmodel
import app.cash.sqldelight.coroutines.asFlow import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList import app.cash.sqldelight.coroutines.mapToList
import dev.msfjarvis.claw.core.injection.DatabaseDispatcher import dev.msfjarvis.claw.core.injection.DatabaseReadDispatcher
import dev.msfjarvis.claw.core.injection.DatabaseWriteDispatcher
import dev.msfjarvis.claw.database.local.SavedPost import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.database.local.SavedPostQueries import dev.msfjarvis.claw.database.local.SavedPostQueries
import dev.msfjarvis.claw.model.UIPost import dev.msfjarvis.claw.model.UIPost
@ -23,23 +24,24 @@ class SavedPostsRepository
@Inject @Inject
constructor( constructor(
private val savedPostQueries: SavedPostQueries, private val savedPostQueries: SavedPostQueries,
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher, @DatabaseReadDispatcher private val readDispatcher: CoroutineDispatcher,
@DatabaseWriteDispatcher private val writeDispatcher: CoroutineDispatcher,
) { ) {
val savedPosts = savedPostQueries.selectAllPosts().asFlow().mapToList(dbDispatcher) val savedPosts = savedPostQueries.selectAllPosts().asFlow().mapToList(readDispatcher)
suspend fun toggleSave(post: UIPost) { suspend fun toggleSave(post: UIPost) {
if (savedPosts.firstOrNull().orEmpty().any { it.shortId == post.shortId }) { if (savedPosts.firstOrNull().orEmpty().any { it.shortId == post.shortId }) {
Napier.d(tag = TAG) { "Removing post: ${post.shortId}" } Napier.d(tag = TAG) { "Removing post: ${post.shortId}" }
withContext(dbDispatcher) { savedPostQueries.deletePost(post.shortId) } withContext(writeDispatcher) { savedPostQueries.deletePost(post.shortId) }
} else { } else {
Napier.d(tag = TAG) { "Saving post: ${post.shortId}" } Napier.d(tag = TAG) { "Saving post: ${post.shortId}" }
withContext(dbDispatcher) { savedPostQueries.insertOrReplacePost(post.toSavedPost()) } withContext(writeDispatcher) { savedPostQueries.insertOrReplacePost(post.toSavedPost()) }
} }
} }
suspend fun savePosts(posts: List<SavedPost>) { suspend fun savePosts(posts: List<SavedPost>) {
Napier.d(tag = TAG) { "Saving posts: ${posts.joinToString(",") { it.shortId }}" } Napier.d(tag = TAG) { "Saving posts: ${posts.joinToString(",") { it.shortId }}" }
withContext(dbDispatcher) { withContext(writeDispatcher) {
savedPostQueries.transaction { savedPostQueries.transaction {
posts.forEach { post -> savedPostQueries.insertOrReplacePost(post) } posts.forEach { post -> savedPostQueries.insertOrReplacePost(post) }
} }

View file

@ -21,8 +21,11 @@ interface DispatcherProvider {
fun io(): CoroutineDispatcher = Dispatchers.IO fun io(): CoroutineDispatcher = Dispatchers.IO
fun database(): CoroutineDispatcher = fun databaseRead(): CoroutineDispatcher =
Dispatchers.IO.limitedParallelism(1, name = "DatabaseDispatcher") Dispatchers.IO.limitedParallelism(4, name = "DatabaseRead")
fun databaseWrite(): CoroutineDispatcher =
Dispatchers.IO.limitedParallelism(1, name = "DatabaseWrite")
} }
/** Concrete type for [DispatcherProvider] with all the defaults from the class. */ /** Concrete type for [DispatcherProvider] with all the defaults from the class. */

View file

@ -16,7 +16,9 @@ import dev.msfjarvis.claw.core.coroutines.DispatcherProvider
import javax.inject.Qualifier import javax.inject.Qualifier
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DatabaseDispatcher @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DatabaseReadDispatcher
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DatabaseWriteDispatcher
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class MainDispatcher @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class MainDispatcher
@ -36,9 +38,16 @@ interface CoroutineDispatcherModule {
return dispatcherProvider.io() return dispatcherProvider.io()
} }
@[Provides DatabaseDispatcher] @[Provides DatabaseReadDispatcher]
fun provideDatabaseDispatcher(dispatcherProvider: DispatcherProvider): CoroutineDispatcher { fun provideDatabaseReadDispatcher(dispatcherProvider: DispatcherProvider): CoroutineDispatcher {
return dispatcherProvider.database() return dispatcherProvider.databaseRead()
}
@[Provides DatabaseWriteDispatcher]
fun provideDatabaseWriteDispatcher(
dispatcherProvider: DispatcherProvider
): CoroutineDispatcher {
return dispatcherProvider.databaseWrite()
} }
@[Provides MainDispatcher] @[Provides MainDispatcher]