mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-12-04 16:00:10 +00:00
Compare commits
No commits in common. "daa6e4aff52ec6d3448d54ece7f914b173a20cc4" and "0473fa238ca6b8c59c158c448dc3ba4d4123385d" have entirely different histories.
daa6e4aff5
...
0473fa238c
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,15 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 0.11.10
|
|
||||||
|
|
||||||
* `Repos`:
|
|
||||||
* `Cache`:
|
|
||||||
* `KVCache` has been replaced to the package `dev.inmo.micro_utils.repos.cache`
|
|
||||||
* `SimpleKVCache` has been replaced to the package `dev.inmo.micro_utils.repos.cache`
|
|
||||||
* New `KVCache` subtype - `FullKVCache`
|
|
||||||
* Add `Full*` variants of standard repos
|
|
||||||
* Add `cached`/`caching` (for write repos) extensions for all standard types of repos
|
|
||||||
|
|
||||||
## 0.11.9
|
## 0.11.9
|
||||||
|
|
||||||
* `Versions`
|
* `Versions`
|
||||||
|
@ -41,13 +41,6 @@ data class Optional<T> internal constructor(
|
|||||||
inline val <T> T.optional
|
inline val <T> T.optional
|
||||||
get() = Optional.presented(this)
|
get() = Optional.presented(this)
|
||||||
|
|
||||||
inline val <T : Any> T?.optionalOrAbsentIfNull
|
|
||||||
get() = if (this == null) {
|
|
||||||
Optional.absent<T>()
|
|
||||||
} else {
|
|
||||||
Optional.presented(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will call [block] when data presented ([Optional.dataPresented] == true)
|
* Will call [block] when data presented ([Optional.dataPresented] == true)
|
||||||
*/
|
*/
|
||||||
|
@ -14,5 +14,5 @@ crypto_js_version=4.1.1
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.11.10
|
version=0.11.9
|
||||||
android_code_version=134
|
android_code_version=133
|
||||||
|
2
repos/cache/build.gradle
vendored
2
repos/cache/build.gradle
vendored
@ -15,4 +15,4 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,15 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache
|
package dev.inmo.micro_utils.repos.cache
|
||||||
|
|
||||||
import dev.inmo.micro_utils.repos.*
|
import dev.inmo.micro_utils.repos.*
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
open class ReadCRUDCacheRepo<ObjectType, IdType>(
|
open class ReadCRUDCacheRepo<ObjectType, IdType>(
|
||||||
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
|
protected val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
|
||||||
protected open val kvCache: KVCache<IdType, ObjectType>,
|
protected val kvCache: KVCache<IdType, ObjectType>,
|
||||||
protected open val idGetter: (ObjectType) -> IdType
|
protected val idGetter: (ObjectType) -> IdType
|
||||||
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo {
|
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo {
|
||||||
override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also {
|
override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also {
|
||||||
kvCache.set(id, it)
|
kvCache.set(id, it)
|
||||||
@ -18,63 +18,8 @@ open class ReadCRUDCacheRepo<ObjectType, IdType>(
|
|||||||
override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id)
|
override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
|
|
||||||
kvCache: KVCache<IdType, ObjectType>,
|
|
||||||
idGetter: (ObjectType) -> IdType
|
|
||||||
) = ReadCRUDCacheRepo(this, kvCache, idGetter)
|
|
||||||
|
|
||||||
open class WriteCRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
|
||||||
protected open val parentRepo: WriteCRUDRepo<ObjectType, IdType, InputValueType>,
|
|
||||||
protected open val kvCache: KVCache<IdType, ObjectType>,
|
|
||||||
protected open val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
|
||||||
protected open val idGetter: (ObjectType) -> IdType
|
|
||||||
) : WriteCRUDRepo<ObjectType, IdType, InputValueType> {
|
|
||||||
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 deletedObjectsFlowJob = parentRepo.deletedObjectsIdsFlow.onEach {
|
|
||||||
kvCache.unset(it)
|
|
||||||
}.launchIn(scope)
|
|
||||||
|
|
||||||
override suspend fun deleteById(ids: List<IdType>) = parentRepo.deleteById(ids)
|
|
||||||
|
|
||||||
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
|
|
||||||
val updated = parentRepo.update(values)
|
|
||||||
|
|
||||||
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 {
|
|
||||||
kvCache.unset(id)
|
|
||||||
kvCache.set(idGetter(it), it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
|
|
||||||
val created = parentRepo.create(values)
|
|
||||||
|
|
||||||
kvCache.set(
|
|
||||||
created.associateBy { idGetter(it) }
|
|
||||||
)
|
|
||||||
|
|
||||||
return created
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <ObjectType, IdType, InputType> WriteCRUDRepo<ObjectType, IdType, InputType>.caching(
|
|
||||||
kvCache: KVCache<IdType, ObjectType>,
|
|
||||||
scope: CoroutineScope,
|
|
||||||
idGetter: (ObjectType) -> IdType
|
|
||||||
) = WriteCRUDCacheRepo(this, kvCache, scope, idGetter)
|
|
||||||
|
|
||||||
|
|
||||||
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
||||||
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
|
parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
|
||||||
kvCache: KVCache<IdType, ObjectType>,
|
kvCache: KVCache<IdType, ObjectType>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
idGetter: (ObjectType) -> IdType
|
idGetter: (ObjectType) -> IdType
|
||||||
@ -82,17 +27,8 @@ open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
|||||||
parentRepo,
|
parentRepo,
|
||||||
kvCache,
|
kvCache,
|
||||||
idGetter
|
idGetter
|
||||||
),
|
), CRUDRepo<ObjectType, IdType, InputValueType>, WriteCRUDRepo<ObjectType, IdType, InputValueType> by parentRepo {
|
||||||
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
|
protected val onNewJob = parentRepo.newObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
|
||||||
parentRepo,
|
protected val onUpdatedJob = parentRepo.updatedObjectsFlow.onEach { kvCache.set(idGetter(it), it) }.launchIn(scope)
|
||||||
kvCache,
|
protected val onRemoveJob = parentRepo.deletedObjectsIdsFlow.onEach { kvCache.unset(it) }.launchIn(scope)
|
||||||
scope,
|
}
|
||||||
idGetter
|
|
||||||
),
|
|
||||||
CRUDRepo<ObjectType, IdType, InputValueType>
|
|
||||||
|
|
||||||
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
|
|
||||||
kvCache: KVCache<IdType, ObjectType>,
|
|
||||||
scope: CoroutineScope,
|
|
||||||
idGetter: (ObjectType) -> IdType
|
|
||||||
) = CRUDCacheRepo(this, kvCache, scope, idGetter)
|
|
||||||
|
@ -1,6 +1,41 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache
|
package dev.inmo.micro_utils.repos.cache
|
||||||
|
|
||||||
@Deprecated("Replaced", ReplaceWith("KVCache", "dev.inmo.micro_utils.repos.cache.cache.KVCache"))
|
import dev.inmo.micro_utils.repos.*
|
||||||
typealias KVCache<K, V> = dev.inmo.micro_utils.repos.cache.cache.KVCache<K, V>
|
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
|
||||||
@Deprecated("Replaced", ReplaceWith("SimpleKVCache", "dev.inmo.micro_utils.repos.cache.cache.SimpleKVCache"))
|
import kotlinx.coroutines.sync.Mutex
|
||||||
typealias SimpleKVCache<K, V> = dev.inmo.micro_utils.repos.cache.cache.SimpleKVCache<K, V>
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
|
||||||
|
interface KVCache<K, V> : KeyValueRepo<K, V>
|
||||||
|
|
||||||
|
open class SimpleKVCache<K, V>(
|
||||||
|
protected val cachedValuesCount: Int,
|
||||||
|
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
|
||||||
|
) : KVCache<K, V>, KeyValueRepo<K, V> by kvParent {
|
||||||
|
protected open val cacheStack = ArrayList<K>(cachedValuesCount)
|
||||||
|
protected val syncMutex = Mutex()
|
||||||
|
|
||||||
|
protected suspend fun makeUnset(toUnset: List<K>) {
|
||||||
|
cacheStack.removeAll(toUnset)
|
||||||
|
kvParent.unset(toUnset)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun set(toSet: Map<K, V>) {
|
||||||
|
syncMutex.withLock {
|
||||||
|
if (toSet.size > cachedValuesCount) {
|
||||||
|
cacheStack.clear()
|
||||||
|
|
||||||
|
kvParent.unset(getAllWithNextPaging { kvParent.keys(it) })
|
||||||
|
val keysToInclude = toSet.keys.drop(toSet.size - cachedValuesCount)
|
||||||
|
|
||||||
|
cacheStack.addAll(keysToInclude)
|
||||||
|
kvParent.set(keysToInclude.associateWith { toSet.getValue(it) })
|
||||||
|
} else {
|
||||||
|
makeUnset(cacheStack.take(toSet.size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unset(toUnset: List<K>) {
|
||||||
|
syncMutex.withLock { makeUnset(toUnset) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,25 +1,18 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache
|
package dev.inmo.micro_utils.repos.cache
|
||||||
|
|
||||||
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.*
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
||||||
open class ReadKeyValueCacheRepo<Key,Value>(
|
open class ReadKeyValueCacheRepo<Key,Value>(
|
||||||
protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
|
protected val parentRepo: ReadKeyValueRepo<Key, Value>,
|
||||||
protected open val kvCache: KVCache<Key, Value>,
|
protected val kvCache: KVCache<Key, Value>,
|
||||||
) : ReadKeyValueRepo<Key,Value> by parentRepo {
|
) : ReadKeyValueRepo<Key,Value> by parentRepo {
|
||||||
override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) }
|
override suspend fun get(k: Key): Value? = kvCache.get(k) ?: parentRepo.get(k) ?.also { kvCache.set(k, it) }
|
||||||
override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key)
|
override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
|
|
||||||
kvCache: KVCache<Key, Value>
|
|
||||||
) = ReadKeyValueCacheRepo(this, kvCache)
|
|
||||||
|
|
||||||
open class KeyValueCacheRepo<Key,Value>(
|
open class KeyValueCacheRepo<Key,Value>(
|
||||||
parentRepo: KeyValueRepo<Key, Value>,
|
parentRepo: KeyValueRepo<Key, Value>,
|
||||||
kvCache: KVCache<Key, Value>,
|
kvCache: KVCache<Key, Value>,
|
||||||
@ -28,8 +21,3 @@ open class KeyValueCacheRepo<Key,Value>(
|
|||||||
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
|
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
|
||||||
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
|
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <Key, Value> KeyValueRepo<Key, Value>.cached(
|
|
||||||
kvCache: KVCache<Key, Value>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) = KeyValueCacheRepo(this, kvCache, scope)
|
|
||||||
|
@ -5,14 +5,13 @@ import dev.inmo.micro_utils.pagination.PaginationResult
|
|||||||
import dev.inmo.micro_utils.pagination.utils.paginate
|
import dev.inmo.micro_utils.pagination.utils.paginate
|
||||||
import dev.inmo.micro_utils.pagination.utils.reverse
|
import dev.inmo.micro_utils.pagination.utils.reverse
|
||||||
import dev.inmo.micro_utils.repos.*
|
import dev.inmo.micro_utils.repos.*
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
||||||
open class ReadKeyValuesCacheRepo<Key,Value>(
|
open class ReadKeyValuesCacheRepo<Key,Value>(
|
||||||
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
|
protected val parentRepo: ReadKeyValuesRepo<Key, Value>,
|
||||||
protected open val kvCache: KVCache<Key, List<Value>>
|
protected val kvCache: KVCache<Key, List<Value>>
|
||||||
) : ReadKeyValuesRepo<Key,Value> by parentRepo {
|
) : ReadKeyValuesRepo<Key,Value> by parentRepo {
|
||||||
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
|
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
|
||||||
return kvCache.get(k) ?.paginate(
|
return kvCache.get(k) ?.paginate(
|
||||||
@ -30,10 +29,6 @@ open class ReadKeyValuesCacheRepo<Key,Value>(
|
|||||||
override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k)
|
override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
|
|
||||||
kvCache: KVCache<Key, List<Value>>
|
|
||||||
) = ReadKeyValuesCacheRepo(this, kvCache)
|
|
||||||
|
|
||||||
open class KeyValuesCacheRepo<Key,Value>(
|
open class KeyValuesCacheRepo<Key,Value>(
|
||||||
parentRepo: KeyValuesRepo<Key, Value>,
|
parentRepo: KeyValuesRepo<Key, Value>,
|
||||||
kvCache: KVCache<Key, List<Value>>,
|
kvCache: KVCache<Key, List<Value>>,
|
||||||
@ -43,8 +38,3 @@ open class KeyValuesCacheRepo<Key,Value>(
|
|||||||
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.minus(it.second) ?: return@onEach) }.launchIn(scope)
|
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.set(it.first, kvCache.get(it.first) ?.minus(it.second) ?: return@onEach) }.launchIn(scope)
|
||||||
protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope)
|
protected val onDataClearedJob = parentRepo.onDataCleared.onEach { kvCache.unset(it) }.launchIn(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <Key, Value> KeyValuesRepo<Key, Value>.cached(
|
|
||||||
kvCache: KVCache<Key, List<Value>>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) = KeyValuesCacheRepo(this, kvCache, scope)
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache.cache
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This interface declares that current type of [KVCache] will contains all the data all the time of its life
|
|
||||||
*/
|
|
||||||
interface FullKVCache<K, V> : KVCache<K, V> {
|
|
||||||
companion object
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache.cache
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.repos.*
|
|
||||||
|
|
||||||
interface KVCache<K, V> : KeyValueRepo<K, V> {
|
|
||||||
companion object
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache.cache
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.repos.KeyValueRepo
|
|
||||||
import dev.inmo.micro_utils.repos.MapKeyValueRepo
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
|
|
||||||
open class SimpleFullKVCache<K, V>(
|
|
||||||
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
|
|
||||||
) : FullKVCache<K, V>, KeyValueRepo<K, V> by kvParent {
|
|
||||||
protected val syncMutex = Mutex()
|
|
||||||
|
|
||||||
override suspend fun set(toSet: Map<K, V>) {
|
|
||||||
syncMutex.withLock {
|
|
||||||
kvParent.set(toSet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun unset(toUnset: List<K>) {
|
|
||||||
syncMutex.withLock {
|
|
||||||
kvParent.unset(toUnset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <K, V> FullKVCache(
|
|
||||||
kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
|
|
||||||
) = SimpleFullKVCache<K, V>(kvParent)
|
|
@ -1,42 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache.cache
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
|
|
||||||
import dev.inmo.micro_utils.repos.*
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
|
|
||||||
open class SimpleKVCache<K, V>(
|
|
||||||
protected val cachedValuesCount: Int,
|
|
||||||
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
|
|
||||||
) : KVCache<K, V>, KeyValueRepo<K, V> by kvParent {
|
|
||||||
protected open val cacheQueue = ArrayDeque<K>(cachedValuesCount)
|
|
||||||
protected val syncMutex = Mutex()
|
|
||||||
|
|
||||||
protected suspend fun makeUnset(toUnset: List<K>) {
|
|
||||||
cacheQueue.removeAll(toUnset)
|
|
||||||
kvParent.unset(toUnset)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun set(toSet: Map<K, V>) {
|
|
||||||
syncMutex.withLock {
|
|
||||||
for ((k, v) in toSet) {
|
|
||||||
if (cacheQueue.size >= cachedValuesCount) {
|
|
||||||
cacheQueue.removeFirstOrNull() ?.let {
|
|
||||||
kvParent.unset(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cacheQueue.addLast(k)
|
|
||||||
kvParent.set(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun unset(toUnset: List<K>) {
|
|
||||||
syncMutex.withLock { makeUnset(toUnset) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <K, V> KVCache(
|
|
||||||
cachedValuesCount: Int,
|
|
||||||
kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
|
|
||||||
) = SimpleKVCache<K, V>(cachedValuesCount, kvParent)
|
|
@ -1,95 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache.full
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.*
|
|
||||||
import dev.inmo.micro_utils.pagination.Pagination
|
|
||||||
import dev.inmo.micro_utils.pagination.PaginationResult
|
|
||||||
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
|
|
||||||
import dev.inmo.micro_utils.repos.*
|
|
||||||
import dev.inmo.micro_utils.repos.cache.*
|
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.KVCache
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
|
|
||||||
open class FullReadCRUDCacheRepo<ObjectType, IdType>(
|
|
||||||
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
|
|
||||||
protected open val kvCache: FullKVCache<IdType, ObjectType>,
|
|
||||||
protected open val idGetter: (ObjectType) -> IdType
|
|
||||||
) : ReadCRUDRepo<ObjectType, IdType> {
|
|
||||||
protected inline fun <T> doOrTakeAndActualize(
|
|
||||||
action: FullKVCache<IdType, ObjectType>.() -> Optional<T>,
|
|
||||||
actionElse: ReadCRUDRepo<ObjectType, IdType>.() -> T,
|
|
||||||
actualize: FullKVCache<IdType, ObjectType>.(T) -> Unit
|
|
||||||
): T {
|
|
||||||
kvCache.action().onPresented {
|
|
||||||
return it
|
|
||||||
}.onAbsent {
|
|
||||||
return parentRepo.actionElse().also {
|
|
||||||
kvCache.actualize(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error("The result should be returned above")
|
|
||||||
}
|
|
||||||
|
|
||||||
protected suspend fun actualizeAll() {
|
|
||||||
kvCache.clear()
|
|
||||||
doForAllWithNextPaging {
|
|
||||||
parentRepo.getByPagination(it).also {
|
|
||||||
kvCache.set(it.results.associateBy { idGetter(it) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 count(): Long = doOrTakeAndActualize(
|
|
||||||
{ count().takeIf { it != 0L }.optionalOrAbsentIfNull },
|
|
||||||
{ count() },
|
|
||||||
{ if (it != 0L) actualizeAll() }
|
|
||||||
)
|
|
||||||
|
|
||||||
override suspend fun contains(id: IdType): Boolean = doOrTakeAndActualize(
|
|
||||||
{ contains(id).takeIf { it }.optionalOrAbsentIfNull },
|
|
||||||
{ contains(id) },
|
|
||||||
{ if (it) parentRepo.getById(id) ?.let { set(id, it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
override suspend fun getById(id: IdType): ObjectType? = doOrTakeAndActualize(
|
|
||||||
{ get(id) ?.optional ?: Optional.absent() },
|
|
||||||
{ getById(id) },
|
|
||||||
{ it ?.let { set(idGetter(it), it) } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
|
|
||||||
kvCache: FullKVCache<IdType, ObjectType>,
|
|
||||||
idGetter: (ObjectType) -> IdType
|
|
||||||
) = FullReadCRUDCacheRepo(this, kvCache, idGetter)
|
|
||||||
|
|
||||||
open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
|
||||||
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
|
|
||||||
kvCache: FullKVCache<IdType, ObjectType>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
|
||||||
idGetter: (ObjectType) -> IdType
|
|
||||||
) : FullReadCRUDCacheRepo<ObjectType, IdType>(
|
|
||||||
parentRepo,
|
|
||||||
kvCache,
|
|
||||||
idGetter
|
|
||||||
),
|
|
||||||
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
|
|
||||||
parentRepo,
|
|
||||||
kvCache,
|
|
||||||
scope,
|
|
||||||
idGetter
|
|
||||||
),
|
|
||||||
CRUDRepo<ObjectType, IdType, InputValueType>
|
|
||||||
|
|
||||||
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
|
|
||||||
kvCache: FullKVCache<IdType, ObjectType>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
|
||||||
idGetter: (ObjectType) -> IdType
|
|
||||||
) = FullCRUDCacheRepo(this, kvCache, scope, idGetter)
|
|
@ -1,104 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache.full
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.*
|
|
||||||
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.cache.FullKVCache
|
|
||||||
import dev.inmo.micro_utils.repos.pagination.getAll
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.*
|
|
||||||
|
|
||||||
open class FullReadKeyValueCacheRepo<Key,Value>(
|
|
||||||
protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
|
|
||||||
protected open val kvCache: FullKVCache<Key, Value>,
|
|
||||||
) : ReadKeyValueRepo<Key, Value> {
|
|
||||||
protected inline fun <T> doOrTakeAndActualize(
|
|
||||||
action: FullKVCache<Key, Value>.() -> Optional<T>,
|
|
||||||
actionElse: ReadKeyValueRepo<Key, Value>.() -> T,
|
|
||||||
actualize: FullKVCache<Key, Value>.(T) -> Unit
|
|
||||||
): T {
|
|
||||||
kvCache.action().onPresented {
|
|
||||||
return it
|
|
||||||
}.onAbsent {
|
|
||||||
return parentRepo.actionElse().also {
|
|
||||||
kvCache.actualize(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error("The result should be returned above")
|
|
||||||
}
|
|
||||||
protected suspend fun actualizeAll() {
|
|
||||||
kvCache.clear()
|
|
||||||
kvCache.set(parentRepo.getAll { keys(it) }.toMap())
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun get(k: Key): Value? = doOrTakeAndActualize(
|
|
||||||
{ get(k) ?.optional ?: Optional.absent() },
|
|
||||||
{ get(k) },
|
|
||||||
{ set(k, it ?: return@doOrTakeAndActualize) }
|
|
||||||
)
|
|
||||||
|
|
||||||
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = doOrTakeAndActualize(
|
|
||||||
{ values(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
|
|
||||||
{ values(pagination, reversed) },
|
|
||||||
{ 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(key: Key): Boolean = doOrTakeAndActualize(
|
|
||||||
{ contains(key).takeIf { it }.optionalOrAbsentIfNull },
|
|
||||||
{ contains(key) },
|
|
||||||
{ if (it) parentRepo.get(key) ?.also { kvCache.set(key, it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = doOrTakeAndActualize(
|
|
||||||
{ keys(pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
|
|
||||||
{ keys(pagination, reversed) },
|
|
||||||
{ if (it.results.isNotEmpty()) actualizeAll() }
|
|
||||||
)
|
|
||||||
|
|
||||||
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = doOrTakeAndActualize(
|
|
||||||
{ keys(v, pagination, reversed).takeIf { it.results.isNotEmpty() }.optionalOrAbsentIfNull },
|
|
||||||
{ parentRepo.keys(v, pagination, reversed) },
|
|
||||||
{ if (it.results.isNotEmpty()) actualizeAll() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
|
|
||||||
kvCache: FullKVCache<Key, Value>
|
|
||||||
) = FullReadKeyValueCacheRepo(this, kvCache)
|
|
||||||
|
|
||||||
open class FullWriteKeyValueCacheRepo<Key,Value>(
|
|
||||||
protected open val parentRepo: WriteKeyValueRepo<Key, Value>,
|
|
||||||
protected open val kvCache: FullKVCache<Key, Value>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) : WriteKeyValueRepo<Key, Value> by parentRepo {
|
|
||||||
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope)
|
|
||||||
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Key, Value> WriteKeyValueRepo<Key, Value>.caching(
|
|
||||||
kvCache: FullKVCache<Key, Value>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) = FullWriteKeyValueCacheRepo(this, kvCache, scope)
|
|
||||||
|
|
||||||
open class FullKeyValueCacheRepo<Key,Value>(
|
|
||||||
parentRepo: KeyValueRepo<Key, Value>,
|
|
||||||
kvCache: FullKVCache<Key, Value>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) : FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
|
|
||||||
KeyValueRepo<Key,Value>,
|
|
||||||
ReadKeyValueRepo<Key, Value> by FullReadKeyValueCacheRepo(parentRepo, kvCache) {
|
|
||||||
override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Key, Value> KeyValueRepo<Key, Value>.cached(
|
|
||||||
kvCache: FullKVCache<Key, Value>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) = FullKeyValueCacheRepo(this, kvCache, scope)
|
|
@ -1,154 +0,0 @@
|
|||||||
package dev.inmo.micro_utils.repos.cache.full
|
|
||||||
|
|
||||||
import dev.inmo.micro_utils.common.*
|
|
||||||
import dev.inmo.micro_utils.pagination.*
|
|
||||||
import dev.inmo.micro_utils.pagination.utils.*
|
|
||||||
import dev.inmo.micro_utils.repos.*
|
|
||||||
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
|
|
||||||
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: FullKVCache<Key, List<Value>>,
|
|
||||||
) : ReadKeyValuesRepo<Key, Value> {
|
|
||||||
protected inline fun <T> doOrTakeAndActualize(
|
|
||||||
action: FullKVCache<Key, List<Value>>.() -> Optional<T>,
|
|
||||||
actionElse: ReadKeyValuesRepo<Key, Value>.() -> T,
|
|
||||||
actualize: FullKVCache<Key, List<Value>>.(T) -> Unit
|
|
||||||
): T {
|
|
||||||
kvCache.action().onPresented {
|
|
||||||
return it
|
|
||||||
}.onAbsent {
|
|
||||||
return parentRepo.actionElse().also {
|
|
||||||
kvCache.actualize(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error("The result should be returned above")
|
|
||||||
}
|
|
||||||
|
|
||||||
protected suspend fun actualizeKey(k: Key) {
|
|
||||||
kvCache.set(k, parentRepo.getAll(k))
|
|
||||||
}
|
|
||||||
|
|
||||||
protected suspend fun actualizeAll() {
|
|
||||||
doAllWithCurrentPaging { kvCache.keys(it).also { kvCache.unset(it.results) } }
|
|
||||||
kvCache.set(parentRepo.getAll())
|
|
||||||
}
|
|
||||||
|
|
||||||
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() }
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
|
|
||||||
kvCache: FullKVCache<Key, List<Value>>
|
|
||||||
) = FullReadKeyValuesCacheRepo(this, kvCache)
|
|
||||||
|
|
||||||
open class FullWriteKeyValuesCacheRepo<Key,Value>(
|
|
||||||
protected open val parentRepo: WriteKeyValuesRepo<Key, Value>,
|
|
||||||
protected open val kvCache: FullKVCache<Key, List<Value>>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) : WriteKeyValuesRepo<Key, Value> by parentRepo {
|
|
||||||
protected val onNewJob = parentRepo.onNewValue.onEach {
|
|
||||||
kvCache.set(
|
|
||||||
it.first,
|
|
||||||
kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)
|
|
||||||
)
|
|
||||||
}.launchIn(scope)
|
|
||||||
protected val onRemoveJob = parentRepo.onValueRemoved.onEach {
|
|
||||||
kvCache.set(
|
|
||||||
it.first,
|
|
||||||
kvCache.get(it.first) ?.minus(it.second) ?: return@onEach
|
|
||||||
)
|
|
||||||
}.launchIn(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching(
|
|
||||||
kvCache: FullKVCache<Key, List<Value>>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope)
|
|
||||||
|
|
||||||
open class FullKeyValuesCacheRepo<Key,Value>(
|
|
||||||
parentRepo: KeyValuesRepo<Key, Value>,
|
|
||||||
kvCache: FullKVCache<Key, List<Value>>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope),
|
|
||||||
KeyValuesRepo<Key, Value>,
|
|
||||||
ReadKeyValuesRepo<Key, Value> by FullReadKeyValuesCacheRepo(parentRepo, kvCache) {
|
|
||||||
override suspend fun clearWithValue(v: Value) {
|
|
||||||
doAllWithCurrentPaging {
|
|
||||||
keys(v, it).also {
|
|
||||||
remove(it.results.associateWith { listOf(v) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Key, Value> KeyValuesRepo<Key, Value>.caching(
|
|
||||||
kvCache: FullKVCache<Key, List<Value>>,
|
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
|
||||||
) = FullKeyValuesCacheRepo(this, kvCache, scope)
|
|
@ -48,10 +48,6 @@ interface KeyValueRepo<Key, Value> : ReadKeyValueRepo<Key, Value>, WriteKeyValue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun clear() {
|
|
||||||
doAllWithCurrentPaging { keys(it).also { unset(it.results) } }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
typealias StandardKeyValueRepo<Key,Value> = KeyValueRepo<Key, Value>
|
typealias StandardKeyValueRepo<Key,Value> = KeyValueRepo<Key, Value>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user