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

144 lines
5.7 KiB
Kotlin

package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.coroutines.SmartRWLocker
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.withReadAcquire
import dev.inmo.micro_utils.coroutines.withWriteLock
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.*
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
open class FullReadCRUDCacheRepo<ObjectType, IdType>(
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected open val kvCache: KeyValueRepo<IdType, ObjectType>,
protected val locker: SmartRWLocker = SmartRWLocker(),
protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType>, FullCacheRepo {
protected suspend inline fun <T> doOrTakeAndActualize(
action: KeyValueRepo<IdType, ObjectType>.() -> Optional<T>,
actionElse: ReadCRUDRepo<ObjectType, IdType>.() -> T,
actualize: KeyValueRepo<IdType, ObjectType>.(T) -> Unit
): T {
locker.withReadAcquire {
kvCache.action().onPresented { return it }
}
return parentRepo.actionElse().also {
kvCache.actualize(it)
}
}
protected suspend inline fun <T> doOrTakeAndActualizeWithWriteLock(
action: KeyValueRepo<IdType, ObjectType>.() -> Optional<T>,
actionElse: ReadCRUDRepo<ObjectType, IdType>.() -> T,
actualize: KeyValueRepo<IdType, ObjectType>.(T) -> Unit
): T = doOrTakeAndActualize(
action = action,
actionElse = actionElse,
actualize = { locker.withWriteLock { actualize(it) } }
)
protected open suspend fun actualizeAll() {
kvCache.actualizeAll(parentRepo, locker = locker)
}
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = doOrTakeAndActualize(
{ values(pagination).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ getByPagination(pagination) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> = doOrTakeAndActualize(
{ keys(pagination).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
{ getIdsByPagination(pagination) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
override suspend fun count(): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeAll() }
)
override suspend fun contains(id: IdType): Boolean = doOrTakeAndActualizeWithWriteLock(
{ contains(id).takeIf { it }.optionalOrAbsentIfNull },
{ contains(id) },
{ if (it) parentRepo.getById(id) ?.let { kvCache.set(id, it) } }
)
override suspend fun getAll(): Map<IdType, ObjectType> = doOrTakeAndActualizeWithWriteLock(
{ getAll().takeIf { it.isNotEmpty() }.optionalOrAbsentIfNull },
{ getAll() },
{ kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it } }
)
override suspend fun getById(id: IdType): ObjectType? = doOrTakeAndActualizeWithWriteLock(
{ get(id) ?.optional ?: Optional.absent() },
{ getById(id) },
{ it ?.let { kvCache.set(idGetter(it), it) } }
)
override suspend fun invalidate() {
actualizeAll()
}
}
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
kvCache: KeyValueRepo<IdType, ObjectType>,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType
) = FullReadCRUDCacheRepo(this, kvCache, locker, idGetter)
open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: KeyValueRepo<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType
) : FullReadCRUDCacheRepo<ObjectType, IdType>(
parentRepo,
kvCache,
locker,
idGetter
),
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
parentRepo,
kvCache,
scope,
locker,
idGetter
),
CRUDRepo<ObjectType, IdType, InputValueType> {
init {
if (!skipStartInvalidate) {
scope.launchSafelyWithoutExceptions { invalidate() }
}
}
override suspend fun invalidate() {
actualizeAll()
}
}
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.fullyCached(
kvCache: KeyValueRepo<IdType, ObjectType> = MapKeyValueRepo(),
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType
) = FullCRUDCacheRepo(this, kvCache, scope, skipStartInvalidate, locker, idGetter)
@Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope, idGetter)", "dev.inmo.micro_utils.repos.cache.full.fullyCached"))
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
kvCache: KeyValueRepo<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType
) = fullyCached(kvCache, scope, skipStartInvalidate, locker, idGetter)