MicroUtils/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/CRUDCacheRepo.kt

162 lines
5.6 KiB
Kotlin

package dev.inmo.micro_utils.repos.cache
import dev.inmo.micro_utils.coroutines.SmartRWLocker
import dev.inmo.micro_utils.coroutines.withReadAcquire
import dev.inmo.micro_utils.coroutines.withWriteLock
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
open class ReadCRUDCacheRepo<ObjectType, IdType>(
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected open val kvCache: KVCache<IdType, ObjectType>,
protected val locker: SmartRWLocker = SmartRWLocker(),
protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo, CommonCacheRepo {
override suspend fun getById(id: IdType): ObjectType? = locker.withReadAcquire {
kvCache.get(id)
} ?: (parentRepo.getById(id) ?.also {
locker.withWriteLock {
kvCache.set(id, it)
}
})
override suspend fun getAll(): Map<IdType, ObjectType> {
return locker.withReadAcquire {
kvCache.getAll()
}.takeIf { it.size.toLong() == count() } ?: parentRepo.getAll().also {
locker.withWriteLock {
kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it }
}
}
}
override suspend fun contains(id: IdType): Boolean = locker.withReadAcquire {
kvCache.contains(id)
} || parentRepo.contains(id)
override suspend fun invalidate() = locker.withWriteLock {
kvCache.clear()
}
}
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
kvCache: KVCache<IdType, ObjectType>,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType
) = ReadCRUDCacheRepo(this, kvCache, locker, idGetter)
open class WriteCRUDCacheRepo<ObjectType, IdType, InputValueType>(
protected open val parentRepo: WriteCRUDRepo<ObjectType, IdType, InputValueType>,
protected open val kvCache: KeyValueRepo<IdType, ObjectType>,
protected open val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
protected val locker: SmartRWLocker = SmartRWLocker(),
protected open val idGetter: (ObjectType) -> IdType
) : WriteCRUDRepo<ObjectType, IdType, InputValueType>, CommonCacheRepo {
override val newObjectsFlow: Flow<ObjectType> by parentRepo::newObjectsFlow
override val updatedObjectsFlow: Flow<ObjectType> by parentRepo::updatedObjectsFlow
override val deletedObjectsIdsFlow: Flow<IdType> by parentRepo::deletedObjectsIdsFlow
val createdObjectsFlowJob = parentRepo.newObjectsFlow.onEach {
locker.withWriteLock {
kvCache.set(idGetter(it), it)
}
}.launchIn(scope)
val updatedObjectsFlowJob = parentRepo.updatedObjectsFlow.onEach {
locker.withWriteLock {
kvCache.set(idGetter(it), it)
}
}.launchIn(scope)
val deletedObjectsFlowJob = parentRepo.deletedObjectsIdsFlow.onEach {
locker.withWriteLock {
kvCache.unset(it)
}
}.launchIn(scope)
override suspend fun deleteById(ids: List<IdType>) = parentRepo.deleteById(ids).also {
locker.withWriteLock {
kvCache.unset(ids)
}
}
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
val updated = parentRepo.update(values)
locker.withWriteLock {
kvCache.unset(values.map { it.id })
kvCache.set(updated.associateBy { idGetter(it) })
}
return updated
}
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
return parentRepo.update(id, value) ?.also {
locker.withWriteLock {
kvCache.unset(id)
kvCache.set(idGetter(it), it)
}
}
}
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
val created = parentRepo.create(values)
locker.withWriteLock {
kvCache.set(
created.associateBy { idGetter(it) }
)
}
return created
}
override suspend fun invalidate() = locker.withWriteLock {
kvCache.clear()
}
}
fun <ObjectType, IdType, InputType> WriteCRUDRepo<ObjectType, IdType, InputType>.caching(
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType
) = WriteCRUDCacheRepo(this, kvCache, scope, locker, idGetter)
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType
) : ReadCRUDCacheRepo<ObjectType, IdType>(
parentRepo,
kvCache,
locker,
idGetter
),
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
parentRepo,
kvCache,
scope,
locker,
idGetter
),
CRUDRepo<ObjectType, IdType, InputValueType> {
override suspend fun invalidate() = kvCache.actualizeAll(parentRepo, locker = locker)
}
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType
) = CRUDCacheRepo(this, kvCache, scope, locker, idGetter)