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

215 lines
7.9 KiB
Kotlin
Raw Normal View History

2022-06-29 17:53:49 +00:00
package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.coroutines.SmartRWLocker
2023-07-26 18:35:01 +00:00
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.withReadAcquire
import dev.inmo.micro_utils.coroutines.withWriteLock
2022-06-29 17:53:49 +00:00
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.*
import dev.inmo.micro_utils.repos.*
2024-02-20 18:05:57 +00:00
import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode
2023-01-27 09:15:27 +00:00
import dev.inmo.micro_utils.repos.cache.util.actualizeAll
2022-06-29 17:53:49 +00:00
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
open class FullReadKeyValuesCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected open val kvCache: KeyValueRepo<Key, List<Value>>,
protected val locker: SmartRWLocker = SmartRWLocker(),
2022-06-30 07:59:32 +00:00
) : ReadKeyValuesRepo<Key, Value>, FullCacheRepo {
protected suspend inline fun <T> doOrTakeAndActualize(
action: KeyValueRepo<Key, List<Value>>.() -> Optional<T>,
2022-06-29 17:53:49 +00:00
actionElse: ReadKeyValuesRepo<Key, Value>.() -> T,
actualize: KeyValueRepo<Key, List<Value>>.(T) -> Unit
2022-06-29 17:53:49 +00:00
): T {
locker.withReadAcquire {
kvCache.action().onPresented { return it }
}
return parentRepo.actionElse().also {
2023-08-29 09:06:55 +00:00
kvCache.actualize(it)
2022-06-29 17:53:49 +00:00
}
}
2023-08-29 09:06:55 +00:00
protected suspend inline fun <T> doOrTakeAndActualizeWithWriteLock(
action: KeyValueRepo<Key, List<Value>>.() -> Optional<T>,
actionElse: ReadKeyValuesRepo<Key, Value>.() -> T,
actualize: KeyValueRepo<Key, List<Value>>.(T) -> Unit
): T = doOrTakeAndActualize(
action = action,
actionElse = actionElse,
actualize = { locker.withWriteLock { actualize(it) } }
)
2022-06-29 17:53:49 +00:00
2022-06-30 07:59:32 +00:00
protected open suspend fun actualizeKey(k: Key) {
2024-02-20 18:05:57 +00:00
kvCache.actualizeAll(locker = locker, clearMode = ActualizeAllClearMode.Never) {
mapOf(k to parentRepo.getAll(k))
}
2022-06-29 17:53:49 +00:00
}
2022-06-30 07:59:32 +00:00
protected open suspend fun actualizeAll() {
2024-02-20 18:05:57 +00:00
kvCache.actualizeAll(parentRepo, locker = locker)
2022-06-29 17:53:49 +00:00
}
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
return doOrTakeAndActualize(
{
get(k) ?.paginate(
pagination.let { if (reversed) it.reverse(count(k)) else it }
) ?.let {
if (reversed) it.copy(results = it.results.reversed()) else it
}.optionalOrAbsentIfNull
},
{ get(k, pagination, reversed) },
{ actualizeKey(k) }
)
}
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
return doOrTakeAndActualize(
{
kvCache.keys(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull
},
{ parentRepo.keys(pagination, reversed) },
{ actualizeAll() }
)
}
override suspend fun count(): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeAll() }
)
override suspend fun count(k: Key): Long = doOrTakeAndActualize(
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
{ count() },
{ if (it != 0L) actualizeKey(k) }
)
override suspend fun contains(k: Key, v: Value): Boolean = doOrTakeAndActualize(
{ get(k) ?.contains(v).takeIf { it == true }.optionalOrAbsentIfNull },
{ contains(k, v) },
{ if (it) actualizeKey(k) }
)
override suspend fun contains(k: Key): Boolean = doOrTakeAndActualize(
{ contains(k).takeIf { it }.optionalOrAbsentIfNull },
{ contains(k) },
{ if (it) actualizeKey(k) }
)
override suspend fun keys(
v: Value,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = doOrTakeAndActualize(
{
val keys = getAllWithNextPaging { keys(it) }.filter { get(it) ?.contains(v) == true }.optionallyReverse(reversed)
if (keys.isNotEmpty()) {
keys.paginate(pagination.optionallyReverse(keys.size, reversed)).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull
} else {
Optional.absent()
}
},
{ parentRepo.keys(v, pagination, reversed) },
{ if (it.results.isNotEmpty()) actualizeAll() }
)
2023-01-27 08:45:31 +00:00
override suspend fun invalidate() {
actualizeAll()
}
2022-06-29 17:53:49 +00:00
}
2022-06-29 20:44:44 +00:00
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
kvCache: KeyValueRepo<Key, List<Value>>,
locker: SmartRWLocker = SmartRWLocker(),
) = FullReadKeyValuesCacheRepo(this, kvCache, locker)
2022-06-29 20:44:44 +00:00
2022-06-29 17:53:49 +00:00
open class FullWriteKeyValuesCacheRepo<Key,Value>(
2023-01-29 14:54:32 +00:00
parentRepo: WriteKeyValuesRepo<Key, Value>,
protected open val kvCache: KeyValueRepo<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
protected val locker: SmartRWLocker = SmartRWLocker(),
2022-06-30 07:59:32 +00:00
) : WriteKeyValuesRepo<Key, Value> by parentRepo, FullCacheRepo {
2022-06-29 20:44:44 +00:00
protected val onNewJob = parentRepo.onNewValue.onEach {
locker.withWriteLock {
kvCache.set(
it.first,
kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)
)
}
2022-06-29 20:44:44 +00:00
}.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach {
locker.withWriteLock {
kvCache.set(
it.first,
kvCache.get(it.first)?.minus(it.second) ?: return@onEach
)
}
2022-06-29 20:44:44 +00:00
}.launchIn(scope)
2023-01-27 08:45:31 +00:00
override suspend fun invalidate() {
locker.withWriteLock {
kvCache.clear()
}
2023-01-27 08:45:31 +00:00
}
2022-06-29 17:53:49 +00:00
}
2022-06-29 20:44:44 +00:00
fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching(
kvCache: KeyValueRepo<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
locker: SmartRWLocker = SmartRWLocker(),
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope, locker)
2022-06-29 20:44:44 +00:00
2022-06-29 17:53:49 +00:00
open class FullKeyValuesCacheRepo<Key,Value>(
2023-01-29 14:54:32 +00:00
protected open val parentRepo: KeyValuesRepo<Key, Value>,
kvCache: KeyValueRepo<Key, List<Value>>,
2023-07-26 18:35:01 +00:00
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope, locker),
2022-06-29 20:44:44 +00:00
KeyValuesRepo<Key, Value>,
ReadKeyValuesRepo<Key, Value> by FullReadKeyValuesCacheRepo(parentRepo, kvCache, locker) {
2023-07-26 18:35:01 +00:00
init {
if (!skipStartInvalidate) {
scope.launchSafelyWithoutExceptions { invalidate() }
}
}
2022-06-29 20:44:44 +00:00
override suspend fun clearWithValue(v: Value) {
doAllWithCurrentPaging {
keys(v, it).also {
remove(it.results.associateWith { listOf(v) })
}
}
}
2023-01-27 08:45:31 +00:00
override suspend fun invalidate() {
2024-02-20 18:05:57 +00:00
kvCache.actualizeAll(parentRepo, locker = locker)
2023-01-27 08:45:31 +00:00
}
2023-04-25 13:14:38 +00:00
2024-02-15 10:18:31 +00:00
override suspend fun set(toSet: Map<Key, List<Value>>) {
super<KeyValuesRepo>.set(toSet)
}
2023-04-25 13:14:38 +00:00
override suspend fun removeWithValue(v: Value) {
super<FullWriteKeyValuesCacheRepo>.removeWithValue(v)
}
2022-06-29 17:53:49 +00:00
}
2022-06-29 20:44:44 +00:00
fun <Key, Value> KeyValuesRepo<Key, Value>.fullyCached(
kvCache: KeyValueRepo<Key, List<Value>> = MapKeyValueRepo(),
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
) = FullKeyValuesCacheRepo(this, kvCache, scope, skipStartInvalidate, locker)
@Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope)", "dev.inmo.micro_utils.repos.cache.full.fullyCached"))
2022-06-29 20:44:44 +00:00
fun <Key, Value> KeyValuesRepo<Key, Value>.caching(
kvCache: KeyValueRepo<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
) = FullKeyValuesCacheRepo(this, kvCache, scope, skipStartInvalidate, locker)