Merge pull request #298 from InsanusMokrassar/0.20.2

0.20.2
This commit is contained in:
InsanusMokrassar 2023-08-24 20:10:06 +06:00 committed by GitHub
commit e775b58d41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 629 additions and 330 deletions

View File

@ -1,5 +1,14 @@
# Changelog # Changelog
## 0.20.2
* All main repos uses `SmartRWLocker`
* `Versions`:
* `Serialization`: `1.5.1` -> `1.6.0`
* `Exposed`: `0.42.0` -> `0.42.1`
* `Korlibs`: `4.0.9` -> `4.0.10`
* `Androis SDK`: `33` -> `34`
## 0.20.1 ## 0.20.1
* `SmallTextField`: * `SmallTextField`:

View File

@ -15,5 +15,5 @@ crypto_js_version=4.1.1
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.20.1 version=0.20.2
android_code_version=207 android_code_version=208

View File

@ -1,16 +1,16 @@
[versions] [versions]
kt = "1.9.0" kt = "1.9.0"
kt-serialization = "1.5.1" kt-serialization = "1.6.0"
kt-coroutines = "1.7.3" kt-coroutines = "1.7.3"
kslog = "1.2.0" kslog = "1.2.0"
jb-compose = "1.4.3" jb-compose = "1.4.3"
jb-exposed = "0.42.0" jb-exposed = "0.42.1"
jb-dokka = "1.8.20" jb-dokka = "1.8.20"
korlibs = "4.0.9" korlibs = "4.0.10"
uuid = "0.8.0" uuid = "0.8.0"
ktor = "2.3.3" ktor = "2.3.3"
@ -38,8 +38,8 @@ android-test = "1.1.5"
android-compose-material3 = "1.1.1" android-compose-material3 = "1.1.1"
android-props-minSdk = "21" android-props-minSdk = "21"
android-props-compileSdk = "33" android-props-compileSdk = "34"
android-props-buildTools = "33.0.2" android-props-buildTools = "34.0.0"
[libraries] [libraries]

View File

@ -1,5 +1,8 @@
package dev.inmo.micro_utils.repos.cache 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.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache import dev.inmo.micro_utils.repos.cache.cache.KVCache
import dev.inmo.micro_utils.repos.cache.util.actualizeAll import dev.inmo.micro_utils.repos.cache.util.actualizeAll
@ -10,32 +13,47 @@ import kotlinx.coroutines.flow.*
open class ReadCRUDCacheRepo<ObjectType, IdType>( open class ReadCRUDCacheRepo<ObjectType, IdType>(
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>, protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected open val kvCache: KVCache<IdType, ObjectType>, protected open val kvCache: KVCache<IdType, ObjectType>,
protected val locker: SmartRWLocker = SmartRWLocker(),
protected open val idGetter: (ObjectType) -> IdType protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType> by parentRepo, CommonCacheRepo { ) : ReadCRUDRepo<ObjectType, IdType> by parentRepo, CommonCacheRepo {
override suspend fun getById(id: IdType): ObjectType? = kvCache.get(id) ?: (parentRepo.getById(id) ?.also { override suspend fun getById(id: IdType): ObjectType? = locker.withReadAcquire {
kvCache.set(id, it) kvCache.get(id)
} ?: (parentRepo.getById(id) ?.also {
locker.withWriteLock {
kvCache.set(id, it)
}
}) })
override suspend fun getAll(): Map<IdType, ObjectType> { override suspend fun getAll(): Map<IdType, ObjectType> {
return kvCache.getAll().takeIf { it.size.toLong() == count() } ?: parentRepo.getAll().also { return locker.withReadAcquire {
kvCache.actualizeAll(true) { it } kvCache.getAll()
}.takeIf { it.size.toLong() == count() } ?: parentRepo.getAll().also {
locker.withWriteLock {
kvCache.actualizeAll(true) { it }
}
} }
} }
override suspend fun contains(id: IdType): Boolean = kvCache.contains(id) || parentRepo.contains(id) override suspend fun contains(id: IdType): Boolean = locker.withReadAcquire {
kvCache.contains(id)
} || parentRepo.contains(id)
override suspend fun invalidate() = kvCache.clear() override suspend fun invalidate() = locker.withWriteLock {
kvCache.clear()
}
} }
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached( fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
kvCache: KVCache<IdType, ObjectType>, kvCache: KVCache<IdType, ObjectType>,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType idGetter: (ObjectType) -> IdType
) = ReadCRUDCacheRepo(this, kvCache, idGetter) ) = ReadCRUDCacheRepo(this, kvCache, locker, idGetter)
open class WriteCRUDCacheRepo<ObjectType, IdType, InputValueType>( open class WriteCRUDCacheRepo<ObjectType, IdType, InputValueType>(
protected open val parentRepo: WriteCRUDRepo<ObjectType, IdType, InputValueType>, protected open val parentRepo: WriteCRUDRepo<ObjectType, IdType, InputValueType>,
protected open val kvCache: KVCache<IdType, ObjectType>, protected open val kvCache: KeyValueRepo<IdType, ObjectType>,
protected open val scope: CoroutineScope = CoroutineScope(Dispatchers.Default), protected open val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
protected val locker: SmartRWLocker = SmartRWLocker(),
protected open val idGetter: (ObjectType) -> IdType protected open val idGetter: (ObjectType) -> IdType
) : WriteCRUDRepo<ObjectType, IdType, InputValueType>, CommonCacheRepo { ) : WriteCRUDRepo<ObjectType, IdType, InputValueType>, CommonCacheRepo {
override val newObjectsFlow: Flow<ObjectType> by parentRepo::newObjectsFlow override val newObjectsFlow: Flow<ObjectType> by parentRepo::newObjectsFlow
@ -43,15 +61,15 @@ open class WriteCRUDCacheRepo<ObjectType, IdType, InputValueType>(
override val deletedObjectsIdsFlow: Flow<IdType> by parentRepo::deletedObjectsIdsFlow override val deletedObjectsIdsFlow: Flow<IdType> by parentRepo::deletedObjectsIdsFlow
val createdObjectsFlowJob = parentRepo.newObjectsFlow.onEach { val createdObjectsFlowJob = parentRepo.newObjectsFlow.onEach {
kvCache.set(idGetter(it), it) locker.withWriteLock { kvCache.set(idGetter(it), it) }
}.launchIn(scope) }.launchIn(scope)
val updatedObjectsFlowJob = parentRepo.updatedObjectsFlow.onEach { val updatedObjectsFlowJob = parentRepo.updatedObjectsFlow.onEach {
kvCache.set(idGetter(it), it) locker.withWriteLock { kvCache.set(idGetter(it), it) }
}.launchIn(scope) }.launchIn(scope)
val deletedObjectsFlowJob = parentRepo.deletedObjectsIdsFlow.onEach { val deletedObjectsFlowJob = parentRepo.deletedObjectsIdsFlow.onEach {
kvCache.unset(it) locker.withWriteLock { kvCache.unset(it) }
}.launchIn(scope) }.launchIn(scope)
override suspend fun deleteById(ids: List<IdType>) = parentRepo.deleteById(ids) override suspend fun deleteById(ids: List<IdType>) = parentRepo.deleteById(ids)
@ -59,53 +77,65 @@ open class WriteCRUDCacheRepo<ObjectType, IdType, InputValueType>(
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> { override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
val updated = parentRepo.update(values) val updated = parentRepo.update(values)
kvCache.unset(values.map { it.id }) locker.withWriteLock {
kvCache.set(updated.associateBy { idGetter(it) }) kvCache.unset(values.map { it.id })
kvCache.set(updated.associateBy { idGetter(it) })
}
return updated return updated
} }
override suspend fun update(id: IdType, value: InputValueType): ObjectType? { override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
return parentRepo.update(id, value) ?.also { return parentRepo.update(id, value) ?.also {
kvCache.unset(id) locker.withWriteLock {
kvCache.set(idGetter(it), it) kvCache.unset(id)
kvCache.set(idGetter(it), it)
}
} }
} }
override suspend fun create(values: List<InputValueType>): List<ObjectType> { override suspend fun create(values: List<InputValueType>): List<ObjectType> {
val created = parentRepo.create(values) val created = parentRepo.create(values)
kvCache.set( locker.withWriteLock {
created.associateBy { idGetter(it) } kvCache.set(
) created.associateBy { idGetter(it) }
)
}
return created return created
} }
override suspend fun invalidate() = kvCache.clear() override suspend fun invalidate() = locker.withWriteLock {
kvCache.clear()
}
} }
fun <ObjectType, IdType, InputType> WriteCRUDRepo<ObjectType, IdType, InputType>.caching( fun <ObjectType, IdType, InputType> WriteCRUDRepo<ObjectType, IdType, InputType>.caching(
kvCache: KVCache<IdType, ObjectType>, kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope, scope: CoroutineScope,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType idGetter: (ObjectType) -> IdType
) = WriteCRUDCacheRepo(this, kvCache, scope, idGetter) ) = WriteCRUDCacheRepo(this, kvCache, scope, locker, idGetter)
open class CRUDCacheRepo<ObjectType, IdType, InputValueType>( open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>, override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: KVCache<IdType, ObjectType>, kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType idGetter: (ObjectType) -> IdType
) : ReadCRUDCacheRepo<ObjectType, IdType>( ) : ReadCRUDCacheRepo<ObjectType, IdType>(
parentRepo, parentRepo,
kvCache, kvCache,
locker,
idGetter idGetter
), ),
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo( WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
parentRepo, parentRepo,
kvCache, kvCache,
scope, scope,
locker,
idGetter idGetter
), ),
CRUDRepo<ObjectType, IdType, InputValueType> CRUDRepo<ObjectType, IdType, InputValueType>
@ -113,5 +143,6 @@ open class CRUDCacheRepo<ObjectType, IdType, InputValueType>(
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached( fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
kvCache: KVCache<IdType, ObjectType>, kvCache: KVCache<IdType, ObjectType>,
scope: CoroutineScope, scope: CoroutineScope,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType idGetter: (ObjectType) -> IdType
) = CRUDCacheRepo(this, kvCache, scope, idGetter) ) = CRUDCacheRepo(this, kvCache, scope, locker, idGetter)

View File

@ -1,5 +1,8 @@
package dev.inmo.micro_utils.repos.cache 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.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.KVCache import dev.inmo.micro_utils.repos.cache.cache.KVCache
@ -10,50 +13,82 @@ import kotlinx.coroutines.flow.*
open class ReadKeyValueCacheRepo<Key,Value>( open class ReadKeyValueCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValueRepo<Key, Value>, protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
protected open val kvCache: KVCache<Key, Value>, protected open val kvCache: KVCache<Key, Value>,
protected val locker: SmartRWLocker = SmartRWLocker(),
) : ReadKeyValueRepo<Key,Value> by parentRepo, CommonCacheRepo { ) : ReadKeyValueRepo<Key,Value> by parentRepo, CommonCacheRepo {
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? = locker.withReadAcquire {
override suspend fun contains(key: Key): Boolean = kvCache.contains(key) || parentRepo.contains(key) kvCache.get(k)
} ?: parentRepo.get(k) ?.also {
locker.withWriteLock {
kvCache.set(k, it)
}
}
override suspend fun contains(key: Key): Boolean = locker.withReadAcquire {
kvCache.contains(key)
} || parentRepo.contains(key)
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> { override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
return keys(pagination, reversed).let { return locker.withReadAcquire {
it.changeResultsUnchecked( keys(pagination, reversed).let {
it.results.mapNotNull { it.changeResultsUnchecked(
get(it) it.results.mapNotNull {
} get(it)
) }
)
}
} }
} }
override suspend fun getAll(): Map<Key, Value> = kvCache.getAll().takeIf { override suspend fun getAll(): Map<Key, Value> = locker.withReadAcquire {
kvCache.getAll()
}.takeIf {
it.size.toLong() == count() it.size.toLong() == count()
} ?: parentRepo.getAll().also { } ?: parentRepo.getAll().also {
kvCache.set(it) locker.withWriteLock {
kvCache.set(it)
}
} }
override suspend fun invalidate() = kvCache.clear() override suspend fun invalidate() = locker.withWriteLock {
kvCache.clear()
}
} }
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached( fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
kvCache: KVCache<Key, Value> kvCache: KVCache<Key, Value>,
) = ReadKeyValueCacheRepo(this, kvCache) locker: SmartRWLocker = SmartRWLocker(),
) = ReadKeyValueCacheRepo(this, kvCache, locker)
open class KeyValueCacheRepo<Key,Value>( open class KeyValueCacheRepo<Key,Value>(
override val parentRepo: KeyValueRepo<Key, Value>, override val parentRepo: KeyValueRepo<Key, Value>,
kvCache: KVCache<Key, Value>, kvCache: KVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) : ReadKeyValueCacheRepo<Key,Value>(parentRepo, kvCache), KeyValueRepo<Key,Value>, WriteKeyValueRepo<Key, Value> by parentRepo, CommonCacheRepo { locker: SmartRWLocker = SmartRWLocker(),
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope) ) : ReadKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, locker), KeyValueRepo<Key,Value>, WriteKeyValueRepo<Key, Value> by parentRepo, CommonCacheRepo {
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope) protected val onNewJob = parentRepo.onNewValue.onEach {
locker.withWriteLock {
kvCache.set(it.first, it.second)
}
}.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach {
locker.withWriteLock {
kvCache.unset(it)
}
}.launchIn(scope)
override suspend fun invalidate() = kvCache.clear() override suspend fun invalidate() = locker.withWriteLock {
kvCache.clear()
}
override suspend fun clear() { override suspend fun clear() {
parentRepo.clear() parentRepo.clear()
kvCache.clear() locker.withWriteLock {
kvCache.clear()
}
} }
} }
fun <Key, Value> KeyValueRepo<Key, Value>.cached( fun <Key, Value> KeyValueRepo<Key, Value>.cached(
kvCache: KVCache<Key, Value>, kvCache: KVCache<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) = KeyValueCacheRepo(this, kvCache, scope) locker: SmartRWLocker = SmartRWLocker(),
) = KeyValueCacheRepo(this, kvCache, scope, locker)

View File

@ -1,5 +1,8 @@
package dev.inmo.micro_utils.repos.cache 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.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.* import dev.inmo.micro_utils.pagination.utils.*
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
@ -10,59 +13,85 @@ import kotlinx.coroutines.flow.*
open class ReadKeyValuesCacheRepo<Key,Value>( open class ReadKeyValuesCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>, protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected open val kvCache: KVCache<Key, List<Value>> protected open val kvCache: KVCache<Key, List<Value>>,
protected val locker: SmartRWLocker = SmartRWLocker(),
) : ReadKeyValuesRepo<Key,Value> by parentRepo, CommonCacheRepo { ) : ReadKeyValuesRepo<Key,Value> by parentRepo, CommonCacheRepo {
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 getAll(k, reversed).paginate( return locker.withReadAcquire {
getAll(k, reversed)
}.paginate(
pagination pagination
) )
} }
override suspend fun getAll(k: Key, reversed: Boolean): List<Value> { override suspend fun getAll(k: Key, reversed: Boolean): List<Value> {
return kvCache.get(k) ?.let { return locker.withReadAcquire {
kvCache.get(k)
} ?.let {
if (reversed) it.reversed() else it if (reversed) it.reversed() else it
} ?: parentRepo.getAll(k, reversed).also { } ?: parentRepo.getAll(k, reversed).also {
kvCache.set(k, it) locker.withWriteLock {
kvCache.set(k, it)
}
} }
} }
override suspend fun contains(k: Key, v: Value): Boolean = kvCache.get(k) ?.contains(v) ?: (parentRepo.contains(k, v).also { override suspend fun contains(k: Key, v: Value): Boolean = locker.withReadAcquire {
kvCache.get(k)
} ?.contains(v) ?: (parentRepo.contains(k, v).also {
if (it) { if (it) {
kvCache.unset(k) // clear as invalid locker.withWriteLock {
kvCache.unset(k) // clear as invalid
}
} }
}) })
override suspend fun contains(k: Key): Boolean = kvCache.contains(k) || parentRepo.contains(k) override suspend fun contains(k: Key): Boolean = locker.withReadAcquire {
kvCache.contains(k)
} || parentRepo.contains(k)
override suspend fun invalidate() = kvCache.clear() override suspend fun invalidate() = locker.withWriteLock {
kvCache.clear()
}
} }
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached( fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
kvCache: KVCache<Key, List<Value>> kvCache: KVCache<Key, List<Value>>,
) = ReadKeyValuesCacheRepo(this, kvCache) locker: SmartRWLocker = SmartRWLocker(),
) = ReadKeyValuesCacheRepo(this, kvCache, locker)
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>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) : ReadKeyValuesCacheRepo<Key,Value>(parentRepo, kvCache), KeyValuesRepo<Key,Value>, WriteKeyValuesRepo<Key,Value> by parentRepo, CommonCacheRepo { locker: SmartRWLocker = SmartRWLocker(),
) : ReadKeyValuesCacheRepo<Key,Value>(parentRepo, kvCache, locker), KeyValuesRepo<Key,Value>, WriteKeyValuesRepo<Key,Value> by parentRepo, CommonCacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { (k, v) -> protected val onNewJob = parentRepo.onNewValue.onEach { (k, v) ->
kvCache.set( locker.withWriteLock {
k, kvCache.set(
kvCache.get(k) ?.plus(v) ?: return@onEach k,
) kvCache.get(k) ?.plus(v) ?: return@onEach
)
}
}.launchIn(scope) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { (k, v) -> protected val onRemoveJob = parentRepo.onValueRemoved.onEach { (k, v) ->
kvCache.set( locker.withWriteLock {
k, kvCache.set(
kvCache.get(k) ?.minus(v) ?: return@onEach k,
) kvCache.get(k)?.minus(v) ?: return@onEach
)
}
}.launchIn(scope) }.launchIn(scope)
protected val onDataClearedJob = parentRepo.onDataCleared.onEach { protected val onDataClearedJob = parentRepo.onDataCleared.onEach {
kvCache.unset(it) locker.withWriteLock {
kvCache.unset(it)
}
}.launchIn(scope) }.launchIn(scope)
override suspend fun invalidate() = kvCache.clear() override suspend fun invalidate() = locker.withWriteLock {
kvCache.clear()
}
} }
fun <Key, Value> KeyValuesRepo<Key, Value>.cached( fun <Key, Value> KeyValuesRepo<Key, Value>.cached(
kvCache: KVCache<Key, List<Value>>, kvCache: KVCache<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) = KeyValuesCacheRepo(this, kvCache, scope) locker: SmartRWLocker = SmartRWLocker(),
) = KeyValuesCacheRepo(this, kvCache, scope, locker)

View File

@ -3,6 +3,7 @@ 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 * This interface declares that current type of [KVCache] will contains all the data all the time of its life
*/ */
@Deprecated("This type of KV repos is obsolete and will be removed soon", ReplaceWith("KeyValueRepo<K, V>", "dev.inmo.micro_utils.repos.KeyValueRepo"))
interface FullKVCache<K, V> : KVCache<K, V> { interface FullKVCache<K, V> : KVCache<K, V> {
companion object companion object
} }

View File

@ -5,7 +5,9 @@ import dev.inmo.micro_utils.repos.MapKeyValueRepo
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
open class SimpleFullKVCache<K, V>( @Deprecated("This type of KV repos is obsolete and will be removed soon", ReplaceWith("MapKeyValueRepo<K, V>()", "dev.inmo.micro_utils.repos.MapKeyValueRepo"))
class SimpleFullKVCache<K, V>(
private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>() private val kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) : FullKVCache<K, V>, KeyValueRepo<K, V> by kvParent { ) : FullKVCache<K, V>, KeyValueRepo<K, V> by kvParent {
protected val syncMutex = Mutex() protected val syncMutex = Mutex()
@ -29,6 +31,7 @@ open class SimpleFullKVCache<K, V>(
} }
} }
@Deprecated("This type of KV repos is obsolete and will be removed soon", ReplaceWith("kvParent", "dev.inmo.micro_utils.repos.MapKeyValueRepo"))
inline fun <K, V> FullKVCache( inline fun <K, V> FullKVCache(
kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>() kvParent: KeyValueRepo<K, V> = MapKeyValueRepo<K, V>()
) = SimpleFullKVCache<K, V>(kvParent) ) = SimpleFullKVCache<K, V>(kvParent)

View File

@ -1,8 +1,9 @@
package dev.inmo.micro_utils.repos.cache.fallback.crud package dev.inmo.micro_utils.repos.cache.fallback.crud
import dev.inmo.micro_utils.repos.CRUDRepo import dev.inmo.micro_utils.repos.CRUDRepo
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.WriteCRUDRepo import dev.inmo.micro_utils.repos.WriteCRUDRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@ -10,7 +11,7 @@ import kotlin.time.Duration.Companion.seconds
open class AutoRecacheCRUDRepo<RegisteredObject, Id, InputObject>( open class AutoRecacheCRUDRepo<RegisteredObject, Id, InputObject>(
originalRepo: CRUDRepo<RegisteredObject, Id, InputObject>, originalRepo: CRUDRepo<RegisteredObject, Id, InputObject>,
scope: CoroutineScope, scope: CoroutineScope,
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds, recacheDelay: Long = 60.seconds.inWholeMilliseconds,
actionWrapper: ActionWrapper = ActionWrapper.Direct, actionWrapper: ActionWrapper = ActionWrapper.Direct,
idGetter: (RegisteredObject) -> Id idGetter: (RegisteredObject) -> Id
@ -29,7 +30,7 @@ open class AutoRecacheCRUDRepo<RegisteredObject, Id, InputObject>(
originalRepo: CRUDRepo<RegisteredObject, Id, InputObject>, originalRepo: CRUDRepo<RegisteredObject, Id, InputObject>,
scope: CoroutineScope, scope: CoroutineScope,
originalCallTimeoutMillis: Long, originalCallTimeoutMillis: Long,
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds, recacheDelay: Long = 60.seconds.inWholeMilliseconds,
idGetter: (RegisteredObject) -> Id idGetter: (RegisteredObject) -> Id
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter) ) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter)

View File

@ -3,8 +3,9 @@ package dev.inmo.micro_utils.repos.cache.fallback.crud
import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.pagination.Pagination import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.ReadCRUDRepo import dev.inmo.micro_utils.repos.ReadCRUDRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
import dev.inmo.micro_utils.repos.cache.util.actualizeAll import dev.inmo.micro_utils.repos.cache.util.actualizeAll
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
@ -18,7 +19,7 @@ import kotlin.time.Duration.Companion.seconds
open class AutoRecacheReadCRUDRepo<RegisteredObject, Id>( open class AutoRecacheReadCRUDRepo<RegisteredObject, Id>(
protected open val originalRepo: ReadCRUDRepo<RegisteredObject, Id>, protected open val originalRepo: ReadCRUDRepo<RegisteredObject, Id>,
protected val scope: CoroutineScope, protected val scope: CoroutineScope,
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), protected val kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds, protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds,
protected val actionWrapper: ActionWrapper = ActionWrapper.Direct, protected val actionWrapper: ActionWrapper = ActionWrapper.Direct,
protected val idGetter: (RegisteredObject) -> Id protected val idGetter: (RegisteredObject) -> Id
@ -35,7 +36,7 @@ open class AutoRecacheReadCRUDRepo<RegisteredObject, Id>(
originalRepo: ReadCRUDRepo<RegisteredObject, Id>, originalRepo: ReadCRUDRepo<RegisteredObject, Id>,
scope: CoroutineScope, scope: CoroutineScope,
originalCallTimeoutMillis: Long, originalCallTimeoutMillis: Long,
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds, recacheDelay: Long = 60.seconds.inWholeMilliseconds,
idGetter: (RegisteredObject) -> Id idGetter: (RegisteredObject) -> Id
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter) ) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter)

View File

@ -1,23 +1,17 @@
package dev.inmo.micro_utils.repos.cache.fallback.crud package dev.inmo.micro_utils.repos.cache.fallback.crud
import dev.inmo.micro_utils.coroutines.plus
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.repos.UpdatedValuePair import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.WriteCRUDRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
import dev.inmo.micro_utils.repos.set
import dev.inmo.micro_utils.repos.unset
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
open class AutoRecacheWriteCRUDRepo<RegisteredObject, Id, InputObject>( open class AutoRecacheWriteCRUDRepo<RegisteredObject, Id, InputObject>(
protected val originalRepo: WriteCRUDRepo<RegisteredObject, Id, InputObject>, protected val originalRepo: WriteCRUDRepo<RegisteredObject, Id, InputObject>,
protected val scope: CoroutineScope, protected val scope: CoroutineScope,
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), protected val kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
protected val idGetter: (RegisteredObject) -> Id protected val idGetter: (RegisteredObject) -> Id
) : WriteCRUDRepo<RegisteredObject, Id, InputObject>, FallbackCacheRepo { ) : WriteCRUDRepo<RegisteredObject, Id, InputObject>, FallbackCacheRepo {
override val deletedObjectsIdsFlow: Flow<Id> override val deletedObjectsIdsFlow: Flow<Id>

View File

@ -1,8 +1,8 @@
package dev.inmo.micro_utils.repos.cache.fallback.keyvalue package dev.inmo.micro_utils.repos.cache.fallback.keyvalue
import dev.inmo.micro_utils.repos.KeyValueRepo import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.WriteKeyValueRepo import dev.inmo.micro_utils.repos.WriteKeyValueRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@ -10,7 +10,7 @@ import kotlin.time.Duration.Companion.seconds
open class AutoRecacheKeyValueRepo<Id, RegisteredObject>( open class AutoRecacheKeyValueRepo<Id, RegisteredObject>(
override val originalRepo: KeyValueRepo<Id, RegisteredObject>, override val originalRepo: KeyValueRepo<Id, RegisteredObject>,
scope: CoroutineScope, scope: CoroutineScope,
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds, recacheDelay: Long = 60.seconds.inWholeMilliseconds,
actionWrapper: ActionWrapper = ActionWrapper.Direct, actionWrapper: ActionWrapper = ActionWrapper.Direct,
idGetter: (RegisteredObject) -> Id idGetter: (RegisteredObject) -> Id
@ -29,7 +29,7 @@ open class AutoRecacheKeyValueRepo<Id, RegisteredObject>(
originalRepo: KeyValueRepo<Id, RegisteredObject>, originalRepo: KeyValueRepo<Id, RegisteredObject>,
scope: CoroutineScope, scope: CoroutineScope,
originalCallTimeoutMillis: Long, originalCallTimeoutMillis: Long,
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds, recacheDelay: Long = 60.seconds.inWholeMilliseconds,
idGetter: (RegisteredObject) -> Id idGetter: (RegisteredObject) -> Id
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter) ) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter)

View File

@ -3,8 +3,9 @@ package dev.inmo.micro_utils.repos.cache.fallback.keyvalue
import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.pagination.Pagination import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
import dev.inmo.micro_utils.repos.cache.util.actualizeAll import dev.inmo.micro_utils.repos.cache.util.actualizeAll
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
@ -18,7 +19,7 @@ import kotlin.time.Duration.Companion.seconds
open class AutoRecacheReadKeyValueRepo<Id, RegisteredObject>( open class AutoRecacheReadKeyValueRepo<Id, RegisteredObject>(
protected open val originalRepo: ReadKeyValueRepo<Id, RegisteredObject>, protected open val originalRepo: ReadKeyValueRepo<Id, RegisteredObject>,
protected val scope: CoroutineScope, protected val scope: CoroutineScope,
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), protected val kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds, protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds,
protected val actionWrapper: ActionWrapper = ActionWrapper.Direct, protected val actionWrapper: ActionWrapper = ActionWrapper.Direct,
protected val idGetter: (RegisteredObject) -> Id protected val idGetter: (RegisteredObject) -> Id
@ -35,7 +36,7 @@ open class AutoRecacheReadKeyValueRepo<Id, RegisteredObject>(
originalRepo: ReadKeyValueRepo<Id, RegisteredObject>, originalRepo: ReadKeyValueRepo<Id, RegisteredObject>,
scope: CoroutineScope, scope: CoroutineScope,
originalCallTimeoutMillis: Long, originalCallTimeoutMillis: Long,
kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache(), kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds, recacheDelay: Long = 60.seconds.inWholeMilliseconds,
idGetter: (RegisteredObject) -> Id idGetter: (RegisteredObject) -> Id
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter) ) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis), idGetter)

View File

@ -1,12 +1,8 @@
package dev.inmo.micro_utils.repos.cache.fallback.keyvalue package dev.inmo.micro_utils.repos.cache.fallback.keyvalue
import dev.inmo.micro_utils.coroutines.plus
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.repos.WriteKeyValueRepo import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
import dev.inmo.micro_utils.repos.set
import dev.inmo.micro_utils.repos.unset
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
@ -14,7 +10,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
open class AutoRecacheWriteKeyValueRepo<Id, RegisteredObject>( open class AutoRecacheWriteKeyValueRepo<Id, RegisteredObject>(
protected val originalRepo: WriteKeyValueRepo<Id, RegisteredObject>, protected val originalRepo: WriteKeyValueRepo<Id, RegisteredObject>,
protected val scope: CoroutineScope, protected val scope: CoroutineScope,
protected val kvCache: FullKVCache<Id, RegisteredObject> = FullKVCache() protected val kvCache: KeyValueRepo<Id, RegisteredObject> = MapKeyValueRepo()
) : WriteKeyValueRepo<Id, RegisteredObject>, FallbackCacheRepo { ) : WriteKeyValueRepo<Id, RegisteredObject>, FallbackCacheRepo {
override val onValueRemoved: Flow<Id> override val onValueRemoved: Flow<Id>
get() = (originalRepo.onValueRemoved).distinctUntilChanged() get() = (originalRepo.onValueRemoved).distinctUntilChanged()

View File

@ -1,8 +1,9 @@
package dev.inmo.micro_utils.repos.cache.fallback.keyvalues package dev.inmo.micro_utils.repos.cache.fallback.keyvalues
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.KeyValuesRepo import dev.inmo.micro_utils.repos.KeyValuesRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.WriteKeyValuesRepo import dev.inmo.micro_utils.repos.WriteKeyValuesRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@ -10,7 +11,7 @@ import kotlin.time.Duration.Companion.seconds
open class AutoRecacheKeyValuesRepo<Id, RegisteredObject>( open class AutoRecacheKeyValuesRepo<Id, RegisteredObject>(
override val originalRepo: KeyValuesRepo<Id, RegisteredObject>, override val originalRepo: KeyValuesRepo<Id, RegisteredObject>,
scope: CoroutineScope, scope: CoroutineScope,
kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache(), kvCache: KeyValueRepo<Id, List<RegisteredObject>> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds, recacheDelay: Long = 60.seconds.inWholeMilliseconds,
actionWrapper: ActionWrapper = ActionWrapper.Direct actionWrapper: ActionWrapper = ActionWrapper.Direct
) : AutoRecacheReadKeyValuesRepo<Id, RegisteredObject> ( ) : AutoRecacheReadKeyValuesRepo<Id, RegisteredObject> (
@ -27,7 +28,7 @@ open class AutoRecacheKeyValuesRepo<Id, RegisteredObject>(
originalRepo: KeyValuesRepo<Id, RegisteredObject>, originalRepo: KeyValuesRepo<Id, RegisteredObject>,
scope: CoroutineScope, scope: CoroutineScope,
originalCallTimeoutMillis: Long, originalCallTimeoutMillis: Long,
kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache(), kvCache: KeyValueRepo<Id, List<RegisteredObject>> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds recacheDelay: Long = 60.seconds.inWholeMilliseconds
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis)) ) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis))

View File

@ -10,8 +10,9 @@ import dev.inmo.micro_utils.pagination.firstIndex
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
import dev.inmo.micro_utils.pagination.utils.optionallyReverse import dev.inmo.micro_utils.pagination.utils.optionallyReverse
import dev.inmo.micro_utils.pagination.utils.paginate import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper import dev.inmo.micro_utils.repos.cache.fallback.ActionWrapper
import dev.inmo.micro_utils.repos.cache.util.actualizeAll import dev.inmo.micro_utils.repos.cache.util.actualizeAll
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
@ -25,7 +26,7 @@ import kotlin.time.Duration.Companion.seconds
open class AutoRecacheReadKeyValuesRepo<Id, RegisteredObject>( open class AutoRecacheReadKeyValuesRepo<Id, RegisteredObject>(
protected open val originalRepo: ReadKeyValuesRepo<Id, RegisteredObject>, protected open val originalRepo: ReadKeyValuesRepo<Id, RegisteredObject>,
protected val scope: CoroutineScope, protected val scope: CoroutineScope,
protected val kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache(), protected val kvCache: KeyValueRepo<Id, List<RegisteredObject>> = MapKeyValueRepo(),
protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds, protected val recacheDelay: Long = 60.seconds.inWholeMilliseconds,
protected val actionWrapper: ActionWrapper = ActionWrapper.Direct protected val actionWrapper: ActionWrapper = ActionWrapper.Direct
) : ReadKeyValuesRepo<Id, RegisteredObject>, FallbackCacheRepo { ) : ReadKeyValuesRepo<Id, RegisteredObject>, FallbackCacheRepo {
@ -41,7 +42,7 @@ open class AutoRecacheReadKeyValuesRepo<Id, RegisteredObject>(
originalRepo: ReadKeyValuesRepo<Id, RegisteredObject>, originalRepo: ReadKeyValuesRepo<Id, RegisteredObject>,
scope: CoroutineScope, scope: CoroutineScope,
originalCallTimeoutMillis: Long, originalCallTimeoutMillis: Long,
kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache(), kvCache: KeyValueRepo<Id, List<RegisteredObject>> = MapKeyValueRepo(),
recacheDelay: Long = 60.seconds.inWholeMilliseconds recacheDelay: Long = 60.seconds.inWholeMilliseconds
) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis)) ) : this(originalRepo, scope, kvCache, recacheDelay, ActionWrapper.Timeouted(originalCallTimeoutMillis))

View File

@ -1,15 +1,10 @@
package dev.inmo.micro_utils.repos.cache.fallback.keyvalues package dev.inmo.micro_utils.repos.cache.fallback.keyvalues
import dev.inmo.micro_utils.coroutines.plus
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.pagination.FirstPagePagination
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
import dev.inmo.micro_utils.repos.WriteKeyValuesRepo import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo
import dev.inmo.micro_utils.repos.pagination.maxPagePagination import dev.inmo.micro_utils.repos.pagination.maxPagePagination
import dev.inmo.micro_utils.repos.set
import dev.inmo.micro_utils.repos.unset
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
@ -17,7 +12,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
open class AutoRecacheWriteKeyValuesRepo<Id, RegisteredObject>( open class AutoRecacheWriteKeyValuesRepo<Id, RegisteredObject>(
protected val originalRepo: WriteKeyValuesRepo<Id, RegisteredObject>, protected val originalRepo: WriteKeyValuesRepo<Id, RegisteredObject>,
protected val scope: CoroutineScope, protected val scope: CoroutineScope,
protected val kvCache: FullKVCache<Id, List<RegisteredObject>> = FullKVCache() protected val kvCache: KeyValueRepo<Id, List<RegisteredObject>> = MapKeyValueRepo()
) : WriteKeyValuesRepo<Id, RegisteredObject>, FallbackCacheRepo { ) : WriteKeyValuesRepo<Id, RegisteredObject>, FallbackCacheRepo {
override val onValueRemoved: Flow<Pair<Id, RegisteredObject>> override val onValueRemoved: Flow<Pair<Id, RegisteredObject>>
get() = originalRepo.onValueRemoved get() = originalRepo.onValueRemoved

View File

@ -1,38 +1,39 @@
package dev.inmo.micro_utils.repos.cache.full package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.* 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.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.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult 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.* import dev.inmo.micro_utils.repos.cache.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.util.actualizeAll import dev.inmo.micro_utils.repos.cache.util.actualizeAll
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
open class FullReadCRUDCacheRepo<ObjectType, IdType>( open class FullReadCRUDCacheRepo<ObjectType, IdType>(
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>, protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
protected open val kvCache: FullKVCache<IdType, ObjectType>, protected open val kvCache: KeyValueRepo<IdType, ObjectType>,
protected val locker: SmartRWLocker = SmartRWLocker(),
protected open val idGetter: (ObjectType) -> IdType protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo<ObjectType, IdType>, FullCacheRepo { ) : ReadCRUDRepo<ObjectType, IdType>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize( protected suspend inline fun <T> doOrTakeAndActualize(
action: FullKVCache<IdType, ObjectType>.() -> Optional<T>, action: KeyValueRepo<IdType, ObjectType>.() -> Optional<T>,
actionElse: ReadCRUDRepo<ObjectType, IdType>.() -> T, actionElse: ReadCRUDRepo<ObjectType, IdType>.() -> T,
actualize: FullKVCache<IdType, ObjectType>.(T) -> Unit actualize: KeyValueRepo<IdType, ObjectType>.(T) -> Unit
): T { ): T {
kvCache.action().onPresented { locker.withReadAcquire {
return it kvCache.action().onPresented { return it }
}.onAbsent { }
return parentRepo.actionElse().also { return parentRepo.actionElse().also {
kvCache.actualize(it) locker.withWriteLock { kvCache.actualize(it) }
}
} }
error("The result should be returned above")
} }
protected open suspend fun actualizeAll() { protected open suspend fun actualizeAll() {
kvCache.actualizeAll(parentRepo) locker.withWriteLock { kvCache.actualizeAll(parentRepo) }
} }
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = doOrTakeAndActualize( override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = doOrTakeAndActualize(
@ -77,25 +78,29 @@ open class FullReadCRUDCacheRepo<ObjectType, IdType>(
} }
fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached( fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
kvCache: FullKVCache<IdType, ObjectType>, kvCache: KeyValueRepo<IdType, ObjectType>,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType idGetter: (ObjectType) -> IdType
) = FullReadCRUDCacheRepo(this, kvCache, idGetter) ) = FullReadCRUDCacheRepo(this, kvCache, locker, idGetter)
open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>( open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>, override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
kvCache: FullKVCache<IdType, ObjectType>, kvCache: KeyValueRepo<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false, skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType idGetter: (ObjectType) -> IdType
) : FullReadCRUDCacheRepo<ObjectType, IdType>( ) : FullReadCRUDCacheRepo<ObjectType, IdType>(
parentRepo, parentRepo,
kvCache, kvCache,
locker,
idGetter idGetter
), ),
WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo( WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo(
parentRepo, parentRepo,
kvCache, kvCache,
scope, scope,
locker,
idGetter idGetter
), ),
CRUDRepo<ObjectType, IdType, InputValueType> { CRUDRepo<ObjectType, IdType, InputValueType> {
@ -111,16 +116,18 @@ open class FullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
} }
fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.fullyCached( fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.fullyCached(
kvCache: FullKVCache<IdType, ObjectType> = FullKVCache(), kvCache: KeyValueRepo<IdType, ObjectType> = MapKeyValueRepo(),
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false, skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType idGetter: (ObjectType) -> IdType
) = FullCRUDCacheRepo(this, kvCache, scope, skipStartInvalidate, idGetter) ) = FullCRUDCacheRepo(this, kvCache, scope, skipStartInvalidate, locker, idGetter)
@Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope, idGetter)", "dev.inmo.micro_utils.repos.cache.full.fullyCached")) @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( fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.cached(
kvCache: FullKVCache<IdType, ObjectType>, kvCache: KeyValueRepo<IdType, ObjectType>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false, skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
idGetter: (ObjectType) -> IdType idGetter: (ObjectType) -> IdType
) = fullyCached(kvCache, scope, skipStartInvalidate, idGetter) ) = fullyCached(kvCache, scope, skipStartInvalidate, locker, idGetter)

View File

@ -1,11 +1,13 @@
package dev.inmo.micro_utils.repos.cache.full package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.* 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.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.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult 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.FullKVCache
import dev.inmo.micro_utils.repos.cache.util.actualizeAll import dev.inmo.micro_utils.repos.cache.util.actualizeAll
import dev.inmo.micro_utils.repos.pagination.getAll import dev.inmo.micro_utils.repos.pagination.getAll
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -14,25 +16,26 @@ import kotlinx.coroutines.flow.*
open class FullReadKeyValueCacheRepo<Key,Value>( open class FullReadKeyValueCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValueRepo<Key, Value>, protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, Value>, protected open val kvCache: KeyValueRepo<Key, Value>,
protected val locker: SmartRWLocker = SmartRWLocker()
) : ReadKeyValueRepo<Key, Value>, FullCacheRepo { ) : ReadKeyValueRepo<Key, Value>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize( protected suspend inline fun <T> doOrTakeAndActualize(
action: FullKVCache<Key, Value>.() -> Optional<T>, action: KeyValueRepo<Key, Value>.() -> Optional<T>,
actionElse: ReadKeyValueRepo<Key, Value>.() -> T, actionElse: ReadKeyValueRepo<Key, Value>.() -> T,
actualize: FullKVCache<Key, Value>.(T) -> Unit actualize: KeyValueRepo<Key, Value>.(T) -> Unit
): T { ): T {
kvCache.action().onPresented { locker.withReadAcquire {
return it kvCache.action().onPresented { return it }
}.onAbsent { }
return parentRepo.actionElse().also { return parentRepo.actionElse().also {
kvCache.actualize(it) locker.withWriteLock { kvCache.actualize(it) }
}
} }
error("The result should be returned above")
} }
protected open suspend fun actualizeAll() { protected open suspend fun actualizeAll() {
kvCache.clear() locker.withWriteLock {
kvCache.set(parentRepo.getAll { keys(it) }.toMap()) kvCache.clear()
kvCache.set(parentRepo.getAll { keys(it) }.toMap())
}
} }
override suspend fun get(k: Key): Value? = doOrTakeAndActualize( override suspend fun get(k: Key): Value? = doOrTakeAndActualize(
@ -83,37 +86,51 @@ open class FullReadKeyValueCacheRepo<Key,Value>(
} }
fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached( fun <Key, Value> ReadKeyValueRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, Value> kvCache: KeyValueRepo<Key, Value>,
) = FullReadKeyValueCacheRepo(this, kvCache) locker: SmartRWLocker = SmartRWLocker()
) = FullReadKeyValueCacheRepo(this, kvCache, locker)
open class FullWriteKeyValueCacheRepo<Key,Value>( open class FullWriteKeyValueCacheRepo<Key,Value>(
parentRepo: WriteKeyValueRepo<Key, Value>, parentRepo: WriteKeyValueRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, Value>, protected open val kvCache: KeyValueRepo<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
protected val locker: SmartRWLocker = SmartRWLocker()
) : WriteKeyValueRepo<Key, Value> by parentRepo, FullCacheRepo { ) : WriteKeyValueRepo<Key, Value> by parentRepo, FullCacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { kvCache.set(it.first, it.second) }.launchIn(scope) protected val onNewJob = parentRepo.onNewValue.onEach {
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { kvCache.unset(it) }.launchIn(scope) locker.withWriteLock {
kvCache.set(it.first, it.second)
}
}.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach {
locker.withWriteLock {
kvCache.unset(it)
}
}.launchIn(scope)
override suspend fun invalidate() { override suspend fun invalidate() {
kvCache.clear() locker.withWriteLock {
kvCache.clear()
}
} }
} }
fun <Key, Value> WriteKeyValueRepo<Key, Value>.caching( fun <Key, Value> WriteKeyValueRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, Value>, kvCache: KeyValueRepo<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) = FullWriteKeyValueCacheRepo(this, kvCache, scope) ) = FullWriteKeyValueCacheRepo(this, kvCache, scope)
open class FullKeyValueCacheRepo<Key,Value>( open class FullKeyValueCacheRepo<Key,Value>(
protected open val parentRepo: KeyValueRepo<Key, Value>, protected open val parentRepo: KeyValueRepo<Key, Value>,
kvCache: FullKVCache<Key, Value>, kvCache: KeyValueRepo<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker()
) : FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope), ) : FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
KeyValueRepo<Key,Value>, KeyValueRepo<Key,Value>,
ReadKeyValueRepo<Key, Value> by FullReadKeyValueCacheRepo( ReadKeyValueRepo<Key, Value> by FullReadKeyValueCacheRepo(
parentRepo, parentRepo,
kvCache kvCache,
locker
) { ) {
init { init {
if (!skipStartInvalidate) { if (!skipStartInvalidate) {
@ -124,7 +141,9 @@ open class FullKeyValueCacheRepo<Key,Value>(
override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset) override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset)
override suspend fun invalidate() { override suspend fun invalidate() {
kvCache.actualizeAll(parentRepo) locker.withWriteLock {
kvCache.actualizeAll(parentRepo)
}
} }
override suspend fun clear() { override suspend fun clear() {
@ -134,12 +153,16 @@ open class FullKeyValueCacheRepo<Key,Value>(
} }
fun <Key, Value> KeyValueRepo<Key, Value>.fullyCached( fun <Key, Value> KeyValueRepo<Key, Value>.fullyCached(
kvCache: FullKVCache<Key, Value> = FullKVCache(), kvCache: KeyValueRepo<Key, Value> = MapKeyValueRepo(),
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) = FullKeyValueCacheRepo(this, kvCache, scope) skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker()
) = FullKeyValueCacheRepo(this, kvCache, scope, skipStartInvalidate, locker)
@Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope)", "dev.inmo.micro_utils.repos.cache.full.fullyCached")) @Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope)", "dev.inmo.micro_utils.repos.cache.full.fullyCached"))
fun <Key, Value> KeyValueRepo<Key, Value>.cached( fun <Key, Value> KeyValueRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, Value>, kvCache: KeyValueRepo<Key, Value>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) = fullyCached(kvCache, scope) skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker()
) = fullyCached(kvCache, scope, skipStartInvalidate, locker)

View File

@ -1,11 +1,13 @@
package dev.inmo.micro_utils.repos.cache.full package dev.inmo.micro_utils.repos.cache.full
import dev.inmo.micro_utils.common.* 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.launchSafelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.withReadAcquire
import dev.inmo.micro_utils.coroutines.withWriteLock
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.pagination.utils.* import dev.inmo.micro_utils.pagination.utils.*
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.util.actualizeAll import dev.inmo.micro_utils.repos.cache.util.actualizeAll
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -13,29 +15,32 @@ import kotlinx.coroutines.flow.*
open class FullReadKeyValuesCacheRepo<Key,Value>( open class FullReadKeyValuesCacheRepo<Key,Value>(
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>, protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, List<Value>>, protected open val kvCache: KeyValueRepo<Key, List<Value>>,
protected val locker: SmartRWLocker = SmartRWLocker(),
) : ReadKeyValuesRepo<Key, Value>, FullCacheRepo { ) : ReadKeyValuesRepo<Key, Value>, FullCacheRepo {
protected inline fun <T> doOrTakeAndActualize( protected suspend inline fun <T> doOrTakeAndActualize(
action: FullKVCache<Key, List<Value>>.() -> Optional<T>, action: KeyValueRepo<Key, List<Value>>.() -> Optional<T>,
actionElse: ReadKeyValuesRepo<Key, Value>.() -> T, actionElse: ReadKeyValuesRepo<Key, Value>.() -> T,
actualize: FullKVCache<Key, List<Value>>.(T) -> Unit actualize: KeyValueRepo<Key, List<Value>>.(T) -> Unit
): T { ): T {
kvCache.action().onPresented { locker.withReadAcquire {
return it kvCache.action().onPresented { return it }
}.onAbsent { }
return parentRepo.actionElse().also { return parentRepo.actionElse().also {
kvCache.actualize(it) locker.withWriteLock { kvCache.actualize(it) }
}
} }
error("The result should be returned above")
} }
protected open suspend fun actualizeKey(k: Key) { protected open suspend fun actualizeKey(k: Key) {
kvCache.set(k, parentRepo.getAll(k)) locker.withWriteLock {
kvCache.set(k, parentRepo.getAll(k))
}
} }
protected open suspend fun actualizeAll() { protected open suspend fun actualizeAll() {
kvCache.actualizeAll(parentRepo) locker.withWriteLock {
kvCache.actualizeAll(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> {
@ -109,45 +114,55 @@ open class FullReadKeyValuesCacheRepo<Key,Value>(
} }
fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached( fun <Key, Value> ReadKeyValuesRepo<Key, Value>.cached(
kvCache: FullKVCache<Key, List<Value>> kvCache: KeyValueRepo<Key, List<Value>>,
) = FullReadKeyValuesCacheRepo(this, kvCache) locker: SmartRWLocker = SmartRWLocker(),
) = FullReadKeyValuesCacheRepo(this, kvCache, locker)
open class FullWriteKeyValuesCacheRepo<Key,Value>( open class FullWriteKeyValuesCacheRepo<Key,Value>(
parentRepo: WriteKeyValuesRepo<Key, Value>, parentRepo: WriteKeyValuesRepo<Key, Value>,
protected open val kvCache: FullKVCache<Key, List<Value>>, protected open val kvCache: KeyValueRepo<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
protected val locker: SmartRWLocker = SmartRWLocker(),
) : WriteKeyValuesRepo<Key, Value> by parentRepo, FullCacheRepo { ) : WriteKeyValuesRepo<Key, Value> by parentRepo, FullCacheRepo {
protected val onNewJob = parentRepo.onNewValue.onEach { protected val onNewJob = parentRepo.onNewValue.onEach {
kvCache.set( locker.withWriteLock {
it.first, kvCache.set(
kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second) it.first,
) kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)
)
}
}.launchIn(scope) }.launchIn(scope)
protected val onRemoveJob = parentRepo.onValueRemoved.onEach { protected val onRemoveJob = parentRepo.onValueRemoved.onEach {
kvCache.set( locker.withWriteLock {
it.first, kvCache.set(
kvCache.get(it.first) ?.minus(it.second) ?: return@onEach it.first,
) kvCache.get(it.first)?.minus(it.second) ?: return@onEach
)
}
}.launchIn(scope) }.launchIn(scope)
override suspend fun invalidate() { override suspend fun invalidate() {
kvCache.clear() locker.withWriteLock {
kvCache.clear()
}
} }
} }
fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching( fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, List<Value>>, kvCache: KeyValueRepo<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope) locker: SmartRWLocker = SmartRWLocker(),
) = FullWriteKeyValuesCacheRepo(this, kvCache, scope, locker)
open class FullKeyValuesCacheRepo<Key,Value>( open class FullKeyValuesCacheRepo<Key,Value>(
protected open val parentRepo: KeyValuesRepo<Key, Value>, protected open val parentRepo: KeyValuesRepo<Key, Value>,
kvCache: FullKVCache<Key, List<Value>>, kvCache: KeyValueRepo<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
skipStartInvalidate: Boolean = false skipStartInvalidate: Boolean = false,
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope), locker: SmartRWLocker = SmartRWLocker(),
) : FullWriteKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, scope, locker),
KeyValuesRepo<Key, Value>, KeyValuesRepo<Key, Value>,
ReadKeyValuesRepo<Key, Value> by FullReadKeyValuesCacheRepo(parentRepo, kvCache) { ReadKeyValuesRepo<Key, Value> by FullReadKeyValuesCacheRepo(parentRepo, kvCache, locker) {
init { init {
if (!skipStartInvalidate) { if (!skipStartInvalidate) {
scope.launchSafelyWithoutExceptions { invalidate() } scope.launchSafelyWithoutExceptions { invalidate() }
@ -163,7 +178,9 @@ open class FullKeyValuesCacheRepo<Key,Value>(
} }
override suspend fun invalidate() { override suspend fun invalidate() {
kvCache.actualizeAll(parentRepo) locker.withWriteLock {
kvCache.actualizeAll(parentRepo)
}
} }
override suspend fun removeWithValue(v: Value) { override suspend fun removeWithValue(v: Value) {
@ -172,12 +189,16 @@ open class FullKeyValuesCacheRepo<Key,Value>(
} }
fun <Key, Value> KeyValuesRepo<Key, Value>.fullyCached( fun <Key, Value> KeyValuesRepo<Key, Value>.fullyCached(
kvCache: FullKVCache<Key, List<Value>> = FullKVCache(), kvCache: KeyValueRepo<Key, List<Value>> = MapKeyValueRepo(),
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) = FullKeyValuesCacheRepo(this, kvCache, scope) 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")) @Deprecated("Renamed", ReplaceWith("this.fullyCached(kvCache, scope)", "dev.inmo.micro_utils.repos.cache.full.fullyCached"))
fun <Key, Value> KeyValuesRepo<Key, Value>.caching( fun <Key, Value> KeyValuesRepo<Key, Value>.caching(
kvCache: FullKVCache<Key, List<Value>>, kvCache: KeyValueRepo<Key, List<Value>>,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default) scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
) = FullKeyValuesCacheRepo(this, kvCache, scope) skipStartInvalidate: Boolean = false,
locker: SmartRWLocker = SmartRWLocker(),
) = FullKeyValuesCacheRepo(this, kvCache, scope, skipStartInvalidate, locker)

View File

@ -1,16 +1,8 @@
package dev.inmo.micro_utils.repos.cache.util package dev.inmo.micro_utils.repos.cache.util
import dev.inmo.micro_utils.pagination.FirstPagePagination import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
import dev.inmo.micro_utils.pagination.utils.getAllByWithNextPaging
import dev.inmo.micro_utils.repos.ReadCRUDRepo
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
import dev.inmo.micro_utils.repos.cache.cache.KVCache
import dev.inmo.micro_utils.repos.pagination.getAll
import dev.inmo.micro_utils.repos.set
suspend inline fun <K, V> KVCache<K, V>.actualizeAll( suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
clear: Boolean = true, clear: Boolean = true,
getAll: () -> Map<K, V> getAll: () -> Map<K, V>
) { ) {
@ -23,7 +15,7 @@ suspend inline fun <K, V> KVCache<K, V>.actualizeAll(
) )
} }
suspend inline fun <K, V> KVCache<K, V>.actualizeAll( suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
repo: ReadKeyValueRepo<K, V>, repo: ReadKeyValueRepo<K, V>,
clear: Boolean = true, clear: Boolean = true,
) { ) {
@ -32,7 +24,7 @@ suspend inline fun <K, V> KVCache<K, V>.actualizeAll(
} }
} }
suspend inline fun <K, V> KVCache<K, List<V>>.actualizeAll( suspend inline fun <K, V> KeyValueRepo<K, List<V>>.actualizeAll(
repo: ReadKeyValuesRepo<K, V>, repo: ReadKeyValuesRepo<K, V>,
clear: Boolean = true, clear: Boolean = true,
) { ) {
@ -41,7 +33,7 @@ suspend inline fun <K, V> KVCache<K, List<V>>.actualizeAll(
} }
} }
suspend inline fun <K, V> KVCache<K, V>.actualizeAll( suspend inline fun <K, V> KeyValueRepo<K, V>.actualizeAll(
repo: ReadCRUDRepo<V, K>, repo: ReadCRUDRepo<V, K>,
clear: Boolean = true, clear: Boolean = true,
) { ) {

View File

@ -52,7 +52,9 @@ class KeyValueStore<T : Any> internal constructor (
} }
} }
override fun onSharedPreferenceChanged(sp: SharedPreferences, key: String) { override fun onSharedPreferenceChanged(sp: SharedPreferences?, key: String?) {
sp ?: return
key ?: return
val value = sp.all[key] val value = sp.all[key]
cachedData ?: return cachedData ?: return
if (value != null) { if (value != null) {

View File

@ -1,38 +1,69 @@
package dev.inmo.micro_utils.repos package dev.inmo.micro_utils.repos
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.pagination.* import dev.inmo.micro_utils.pagination.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
/**
* [Map]-based [ReadMapCRUDRepo]. All internal operations will be locked with [locker] (mostly with
* [SmartRWLocker.withReadAcquire])
*
* **Warning**: It is not recommended to use constructor with both [Map] and [SmartRWLocker]. Besides, in case
* you are using your own [Map] as a [map] you should be careful with operations on this [map]
*/
class ReadMapCRUDRepo<ObjectType, IdType>( class ReadMapCRUDRepo<ObjectType, IdType>(
private val map: Map<IdType, ObjectType> = emptyMap() private val map: Map<IdType, ObjectType> = emptyMap(),
private val locker: SmartRWLocker = SmartRWLocker()
) : ReadCRUDRepo<ObjectType, IdType> { ) : ReadCRUDRepo<ObjectType, IdType> {
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> { override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> {
return map.keys.drop(pagination.firstIndex).take(pagination.size).mapNotNull { return locker.withReadAcquire {
map[it] map.keys.drop(pagination.firstIndex).take(pagination.size).mapNotNull {
}.createPaginationResult( map[it]
pagination, }.createPaginationResult(
count() pagination,
) count()
)
}
} }
override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> { override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> {
return map.keys.drop(pagination.firstIndex).take(pagination.size).createPaginationResult( return locker.withReadAcquire {
pagination, map.keys.drop(pagination.firstIndex).take(pagination.size).createPaginationResult(
count() pagination,
) count()
)
}
} }
override suspend fun getById(id: IdType): ObjectType? = map[id] override suspend fun getById(id: IdType): ObjectType? = locker.withReadAcquire {
map[id]
}
override suspend fun contains(id: IdType): Boolean = map.containsKey(id) override suspend fun contains(id: IdType): Boolean = locker.withReadAcquire {
map.containsKey(id)
}
override suspend fun getAll(): Map<IdType, ObjectType> = map.toMap() override suspend fun getAll(): Map<IdType, ObjectType> = locker.withReadAcquire {
map.toMap()
}
override suspend fun count(): Long = map.size.toLong() override suspend fun count(): Long = locker.withReadAcquire {
map.size.toLong()
}
} }
/**
* [MutableMap]-based [WriteMapCRUDRepo]. All internal operations will be locked with [locker] (mostly with
* [SmartRWLocker.withWriteLock])
*
* **Warning**: It is not recommended to use constructor with both [MutableMap] and [SmartRWLocker]. Besides, in case
* you are using your own [MutableMap] as a [map] you should be careful with operations on this [map]
*/
abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>( abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(
protected val map: MutableMap<IdType, ObjectType> = mutableMapOf() protected val map: MutableMap<IdType, ObjectType> = mutableMapOf(),
protected val locker: SmartRWLocker = SmartRWLocker()
) : WriteCRUDRepo<ObjectType, IdType, InputValueType> { ) : WriteCRUDRepo<ObjectType, IdType, InputValueType> {
protected val _newObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow() protected val _newObjectsFlow: MutableSharedFlow<ObjectType> = MutableSharedFlow()
override val newObjectsFlow: Flow<ObjectType> = _newObjectsFlow.asSharedFlow() override val newObjectsFlow: Flow<ObjectType> = _newObjectsFlow.asSharedFlow()
@ -45,21 +76,25 @@ abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(
protected abstract suspend fun createObject(newValue: InputValueType): Pair<IdType, ObjectType> protected abstract suspend fun createObject(newValue: InputValueType): Pair<IdType, ObjectType>
override suspend fun create(values: List<InputValueType>): List<ObjectType> { override suspend fun create(values: List<InputValueType>): List<ObjectType> {
return values.map { return locker.withWriteLock {
val (id, newObject) = createObject(it) values.map {
map[id] = newObject val (id, newObject) = createObject(it)
newObject.also { _ -> map[id] = newObject
_newObjectsFlow.emit(newObject) newObject.also { _ ->
_newObjectsFlow.emit(newObject)
}
} }
} }
} }
override suspend fun update(id: IdType, value: InputValueType): ObjectType? { override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
val newValue = updateObject(value, id, map[id] ?: return null) return locker.withWriteLock {
val newValue = updateObject(value, id, map[id] ?: return@withWriteLock null)
return newValue.also { newValue.also {
map[id] = it map[id] = it
_updatedObjectsFlow.emit(it) _updatedObjectsFlow.emit(it)
}
} }
} }
@ -68,24 +103,40 @@ abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(
} }
override suspend fun deleteById(ids: List<IdType>) { override suspend fun deleteById(ids: List<IdType>) {
ids.forEach { locker.withWriteLock {
map.remove(it) ?.also { _ -> _deletedObjectsIdsFlow.emit(it) } ids.forEach {
map.remove(it) ?.also { _ -> _deletedObjectsIdsFlow.emit(it) }
}
} }
} }
} }
/**
* [MutableMap]-based [MapCRUDRepo]. All internal operations will be locked with [locker]
*
* **Warning**: It is not recommended to use constructor with both [MutableMap] and [SmartRWLocker]. Besides, in case
* you are using your own [MutableMap] as a [map] you should be careful with operations on this [map]
*/
abstract class MapCRUDRepo<ObjectType, IdType, InputValueType>( abstract class MapCRUDRepo<ObjectType, IdType, InputValueType>(
map: MutableMap<IdType, ObjectType> map: MutableMap<IdType, ObjectType>,
locker: SmartRWLocker = SmartRWLocker()
) : CRUDRepo<ObjectType, IdType, InputValueType>, ) : CRUDRepo<ObjectType, IdType, InputValueType>,
ReadCRUDRepo<ObjectType, IdType> by ReadMapCRUDRepo(map), ReadCRUDRepo<ObjectType, IdType> by ReadMapCRUDRepo(map, locker),
WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(map) WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(map, locker)
/**
* [MutableMap]-based [MapCRUDRepo]. All internal operations will be locked with [locker]
*
* **Warning**: Besides, in case you are using your own [MutableMap] as a [map] you should be careful with operations
* on this [map]
*/
fun <ObjectType, IdType, InputValueType> MapCRUDRepo( fun <ObjectType, IdType, InputValueType> MapCRUDRepo(
map: MutableMap<IdType, ObjectType>, map: MutableMap<IdType, ObjectType>,
updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType, updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
locker: SmartRWLocker = SmartRWLocker(),
createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType> createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = object : MapCRUDRepo<ObjectType, IdType, InputValueType>(map) { ) = object : MapCRUDRepo<ObjectType, IdType, InputValueType>(map, locker) {
override suspend fun updateObject( override suspend fun updateObject(
newValue: InputValueType, newValue: InputValueType,
id: IdType, id: IdType,
@ -95,12 +146,23 @@ fun <ObjectType, IdType, InputValueType> MapCRUDRepo(
override suspend fun createObject(newValue: InputValueType): Pair<IdType, ObjectType> = map.createCallback(newValue) override suspend fun createObject(newValue: InputValueType): Pair<IdType, ObjectType> = map.createCallback(newValue)
} }
/**
* [MutableMap]-based [MapCRUDRepo]. All internal operations will be locked with [locker]
*/
fun <ObjectType, IdType, InputValueType> MapCRUDRepo( fun <ObjectType, IdType, InputValueType> MapCRUDRepo(
updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType, updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
locker: SmartRWLocker = SmartRWLocker(),
createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType> createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = MapCRUDRepo(mutableMapOf(), updateCallback, createCallback) ) = MapCRUDRepo(mutableMapOf(), updateCallback, locker, createCallback)
/**
* [MutableMap]-based [MapCRUDRepo]. All internal operations will be locked with [locker]
*
* **Warning**: Besides, in case you are using your own [MutableMap] as a [this] you should be careful with operations
* on this [this]
*/
fun <ObjectType, IdType, InputValueType> MutableMap<IdType, ObjectType>.asCrudRepo( fun <ObjectType, IdType, InputValueType> MutableMap<IdType, ObjectType>.asCrudRepo(
updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType, updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
locker: SmartRWLocker = SmartRWLocker(),
createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType> createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = MapCRUDRepo(this, updateCallback, createCallback) ) = MapCRUDRepo(this, updateCallback, locker, createCallback)

View File

@ -1,5 +1,8 @@
package dev.inmo.micro_utils.repos package dev.inmo.micro_utils.repos
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.pagination.Pagination import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.utils.paginate import dev.inmo.micro_utils.pagination.utils.paginate
@ -7,59 +10,83 @@ import dev.inmo.micro_utils.pagination.utils.reverse
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
/**
* [Map]-based [ReadKeyValueRepo]. All internal operations will be locked with [locker] (mostly with
* [SmartRWLocker.withReadAcquire])
*
* **Warning**: It is not recommended to use constructor with both [Map] and [SmartRWLocker]. Besides, in case you are
* using your own [Map] as a [map] you should be careful with operations on this [map]
*/
class ReadMapKeyValueRepo<Key, Value>( class ReadMapKeyValueRepo<Key, Value>(
protected val map: Map<Key, Value> = emptyMap() protected val map: Map<Key, Value>,
private val locker: SmartRWLocker
) : ReadKeyValueRepo<Key, Value> { ) : ReadKeyValueRepo<Key, Value> {
override suspend fun get(k: Key): Value? = map[k] constructor(map: Map<Key, Value> = emptyMap()) : this(map, SmartRWLocker())
override suspend fun get(k: Key): Value? = locker.withReadAcquire { map[k] }
override suspend fun values( override suspend fun values(
pagination: Pagination, pagination: Pagination,
reversed: Boolean reversed: Boolean
): PaginationResult<Value> { ): PaginationResult<Value> {
val values = map.values return locker.withReadAcquire {
val actualPagination = if (reversed) pagination.reverse(values.size) else pagination val values = map.values
return values.paginate(actualPagination).let { val actualPagination = if (reversed) pagination.reverse(values.size) else pagination
if (reversed) { values.paginate(actualPagination).let {
it.copy(results = it.results.reversed()) if (reversed) {
} else { it.copy(results = it.results.reversed())
it } else {
it
}
} }
} }
} }
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> { override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
val keys = map.keys return locker.withReadAcquire {
val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination val keys = map.keys
return keys.paginate(actualPagination).let { val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination
if (reversed) { keys.paginate(actualPagination).let {
it.copy(results = it.results.reversed()) if (reversed) {
} else { it.copy(results = it.results.reversed())
it } else {
it
}
} }
} }
} }
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> { override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
val keys: List<Key> = map.mapNotNull { (k, value) -> if (v == value) k else null } return locker.withReadAcquire {
val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination val keys: List<Key> = map.mapNotNull { (k, value) -> if (v == value) k else null }
return keys.paginate(actualPagination).let { val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination
if (reversed) { keys.paginate(actualPagination).let {
it.copy(results = it.results.reversed()) if (reversed) {
} else { it.copy(results = it.results.reversed())
it } else {
it
}
} }
} }
} }
override suspend fun getAll(): Map<Key, Value> = map.toMap() override suspend fun getAll(): Map<Key, Value> = locker.withReadAcquire { map.toMap() }
override suspend fun contains(key: Key): Boolean = map.containsKey(key) override suspend fun contains(key: Key): Boolean = locker.withReadAcquire { map.containsKey(key) }
override suspend fun count(): Long = map.size.toLong() override suspend fun count(): Long = locker.withReadAcquire { map.size.toLong() }
} }
/**
* [MutableMap]-based [WriteKeyValueRepo]. All internal operations will be locked with [locker] (mostly with
* [SmartRWLocker.withWriteLock])
*
* **Warning**: It is not recommended to use constructor with both [MutableMap] and [SmartRWLocker]. Besides, in case
* you are using your own [MutableMap] as a [map] you should be careful with operations on this [map]
*/
class WriteMapKeyValueRepo<Key, Value>( class WriteMapKeyValueRepo<Key, Value>(
private val map: MutableMap<Key, Value> = mutableMapOf() private val map: MutableMap<Key, Value>,
private val locker: SmartRWLocker
) : WriteKeyValueRepo<Key, Value> { ) : WriteKeyValueRepo<Key, Value> {
private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow() private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
override val onNewValue: Flow<Pair<Key, Value>> override val onNewValue: Flow<Pair<Key, Value>>
@ -67,37 +94,56 @@ class WriteMapKeyValueRepo<Key, Value>(
private val _onValueRemoved: MutableSharedFlow<Key> = MutableSharedFlow() private val _onValueRemoved: MutableSharedFlow<Key> = MutableSharedFlow()
override val onValueRemoved: Flow<Key> override val onValueRemoved: Flow<Key>
get() = _onValueRemoved get() = _onValueRemoved
constructor(map: MutableMap<Key, Value> = mutableMapOf()) : this(map, SmartRWLocker())
override suspend fun set(toSet: Map<Key, Value>) { override suspend fun set(toSet: Map<Key, Value>) {
map.putAll(toSet) locker.withWriteLock { map.putAll(toSet) }
toSet.forEach { (k, v) -> _onNewValue.emit(k to v) } toSet.forEach { (k, v) -> _onNewValue.emit(k to v) }
} }
override suspend fun unset(toUnset: List<Key>) { override suspend fun unset(toUnset: List<Key>) {
toUnset.forEach { k -> locker.withWriteLock {
map.remove(k) ?.also { _ -> _onValueRemoved.emit(k) } toUnset.forEach { k ->
map.remove(k) ?.also { _ -> _onValueRemoved.emit(k) }
}
} }
} }
override suspend fun unsetWithValues(toUnset: List<Value>) { override suspend fun unsetWithValues(toUnset: List<Value>) {
map.mapNotNull { (k, v) -> locker.withWriteLock {
k.takeIf { v in toUnset } map.mapNotNull { (k, v) ->
}.forEach { k.takeIf { v in toUnset }
map.remove(it) }.forEach {
_onValueRemoved.emit(it) map.remove(it)
_onValueRemoved.emit(it)
}
} }
} }
} }
/**
* [MutableMap]-based [KeyValueRepo]. All internal operations will be locked with [locker]
*
* **Warning**: It is not recommended to use constructor with both [MutableMap] and [SmartRWLocker]. Besides, in case
* you are using your own [MutableMap] as a [map] you should be careful with operations on this [map]
*/
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
class MapKeyValueRepo<Key, Value>( class MapKeyValueRepo<Key, Value>(
private val map: MutableMap<Key, Value> = mutableMapOf() private val map: MutableMap<Key, Value>,
private val locker: SmartRWLocker
) : KeyValueRepo<Key, Value>, ) : KeyValueRepo<Key, Value>,
ReadKeyValueRepo<Key, Value> by ReadMapKeyValueRepo(map), ReadKeyValueRepo<Key, Value> by ReadMapKeyValueRepo(map, locker),
WriteKeyValueRepo<Key, Value> by WriteMapKeyValueRepo(map) { WriteKeyValueRepo<Key, Value> by WriteMapKeyValueRepo(map, locker) {
constructor(map: MutableMap<Key, Value> = mutableMapOf()) : this(map, SmartRWLocker())
override suspend fun clear() { override suspend fun clear() {
map.clear() locker.withWriteLock { map.clear() }
} }
} }
fun <K, V> MutableMap<K, V>.asKeyValueRepo(): KeyValueRepo<K, V> = MapKeyValueRepo(this) /**
* [MutableMap]-based [KeyValueRepo]. All internal operations will be locked with [locker]
*
* **Warning**: It is not recommended to use constructor with both [MutableMap] and [SmartRWLocker]. Besides, in case
* you are using your own [MutableMap] as a [this] you should be careful with operations on this [this]
*/
fun <K, V> MutableMap<K, V>.asKeyValueRepo(locker: SmartRWLocker = SmartRWLocker()): KeyValueRepo<K, V> = MapKeyValueRepo(this, locker)

View File

@ -1,15 +1,28 @@
package dev.inmo.micro_utils.repos package dev.inmo.micro_utils.repos
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.pagination.* import dev.inmo.micro_utils.pagination.*
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 kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
/**
* [Map]-based [ReadKeyValuesRepo]. All internal operations will be locked with [locker] (mostly with
* [SmartRWLocker.withReadAcquire])
*
* **Warning**: It is not recommended to use constructor with both [Map] and [SmartRWLocker]. Besides, in case
* you are using your own [Map] as a [map] you should be careful with operations on this [map]
*/
class MapReadKeyValuesRepo<Key, Value>( class MapReadKeyValuesRepo<Key, Value>(
private val map: Map<Key, List<Value>> = emptyMap() private val map: Map<Key, List<Value>> = emptyMap(),
private val locker: SmartRWLocker = SmartRWLocker()
) : ReadKeyValuesRepo<Key, Value> { ) : ReadKeyValuesRepo<Key, Value> {
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> { override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> {
val list = map[k] ?: return emptyPaginationResult() val list = locker.withReadAcquire {
map[k] ?: return emptyPaginationResult()
}
return list.paginate( return list.paginate(
if (reversed) { if (reversed) {
@ -21,7 +34,9 @@ class MapReadKeyValuesRepo<Key, Value>(
} }
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> { override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
val keys = map.keys val keys = locker.withReadAcquire {
map.keys
}
val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination
return keys.paginate(actualPagination).let { return keys.paginate(actualPagination).let {
if (reversed) { if (reversed) {
@ -33,7 +48,9 @@ class MapReadKeyValuesRepo<Key, Value>(
} }
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> { override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
val keys = map.keys.filter { map[it] ?.contains(v) == true } val keys = locker.withReadAcquire {
map.keys.filter { map[it] ?.contains(v) == true }
}
val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination val actualPagination = if (reversed) pagination.reverse(keys.size) else pagination
return keys.paginate(actualPagination).let { return keys.paginate(actualPagination).let {
if (reversed) { if (reversed) {
@ -44,17 +61,25 @@ class MapReadKeyValuesRepo<Key, Value>(
} }
} }
override suspend fun contains(k: Key): Boolean = map.containsKey(k) override suspend fun contains(k: Key): Boolean = locker.withReadAcquire { map.containsKey(k) }
override suspend fun contains(k: Key, v: Value): Boolean = map[k] ?.contains(v) == true override suspend fun contains(k: Key, v: Value): Boolean = locker.withReadAcquire { map[k] ?.contains(v) } == true
override suspend fun count(k: Key): Long = map[k] ?.size ?.toLong() ?: 0L override suspend fun count(k: Key): Long = locker.withReadAcquire { map[k] ?.size } ?.toLong() ?: 0L
override suspend fun count(): Long = map.size.toLong() override suspend fun count(): Long = locker.withReadAcquire { map.size }.toLong()
} }
/**
* [MutableMap]-based [WriteKeyValuesRepo]. All internal operations will be locked with [locker] (mostly with
* [SmartRWLocker.withWriteLock])
*
* **Warning**: It is not recommended to use constructor with both [MutableMap] and [SmartRWLocker]. Besides, in case
* you are using your own [MutableMap] as a [map] you should be careful with operations on this [map]
*/
class MapWriteKeyValuesRepo<Key, Value>( class MapWriteKeyValuesRepo<Key, Value>(
private val map: MutableMap<Key, MutableList<Value>> = mutableMapOf() private val map: MutableMap<Key, MutableList<Value>> = mutableMapOf(),
private val locker: SmartRWLocker = SmartRWLocker()
) : WriteKeyValuesRepo<Key, Value> { ) : WriteKeyValuesRepo<Key, Value> {
private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow() private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow() override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
@ -64,61 +89,84 @@ class MapWriteKeyValuesRepo<Key, Value>(
override val onDataCleared: Flow<Key> = _onDataCleared.asSharedFlow() override val onDataCleared: Flow<Key> = _onDataCleared.asSharedFlow()
override suspend fun add(toAdd: Map<Key, List<Value>>) { override suspend fun add(toAdd: Map<Key, List<Value>>) {
toAdd.keys.forEach { k -> locker.withWriteLock {
if (map.getOrPut(k) { mutableListOf() }.addAll(toAdd[k] ?: return@forEach)) { toAdd.keys.forEach { k ->
toAdd[k] ?.forEach { v -> if (map.getOrPut(k) { mutableListOf() }.addAll(toAdd[k] ?: return@forEach)) {
_onNewValue.emit(k to v) toAdd[k] ?.forEach { v ->
_onNewValue.emit(k to v)
}
} }
} }
} }
} }
override suspend fun remove(toRemove: Map<Key, List<Value>>) { override suspend fun remove(toRemove: Map<Key, List<Value>>) {
toRemove.keys.forEach { k -> locker.withWriteLock {
if (map[k] ?.removeAll(toRemove[k] ?: return@forEach) == true) { toRemove.keys.forEach { k ->
toRemove[k] ?.forEach { v -> if (map[k]?.removeAll(toRemove[k] ?: return@forEach) == true) {
_onValueRemoved.emit(k to v) toRemove[k]?.forEach { v ->
_onValueRemoved.emit(k to v)
}
}
if (map[k]?.isEmpty() == true) {
map.remove(k)
_onDataCleared.emit(k)
} }
}
if (map[k] ?.isEmpty() == true) {
map.remove(k)
_onDataCleared.emit(k)
} }
} }
} }
override suspend fun removeWithValue(v: Value) { override suspend fun removeWithValue(v: Value) {
map.forEach { (k, values) -> locker.withWriteLock {
if (values.remove(v)) { map.forEach { (k, values) ->
_onValueRemoved.emit(k to v) if (values.remove(v)) {
_onValueRemoved.emit(k to v)
}
} }
} }
} }
override suspend fun clear(k: Key) { override suspend fun clear(k: Key) {
map.remove(k) ?.also { _onDataCleared.emit(k) } locker.withWriteLock {
map.remove(k) ?.also { _onDataCleared.emit(k) }
}
} }
override suspend fun clearWithValue(v: Value) { override suspend fun clearWithValue(v: Value) {
map.filter { (_, values) -> locker.withWriteLock {
values.contains(v) map.filter { (_, values) ->
}.forEach { values.contains(v)
map.remove(it.key) ?.onEach { v -> }.forEach {
_onValueRemoved.emit(it.key to v) map.remove(it.key)?.onEach { v ->
} ?.also { _ -> _onValueRemoved.emit(it.key to v)
_onDataCleared.emit(it.key) }?.also { _ ->
_onDataCleared.emit(it.key)
}
} }
} }
} }
} }
/**
* [MutableMap]-based [KeyValuesRepo]. All internal operations will be locked with [locker]
*
* **Warning**: It is not recommended to use constructor with both [MutableMap] and [SmartRWLocker]. Besides, in case
* you are using your own [MutableMap] as a [map] you should be careful with operations on this [map]
*/
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
class MapKeyValuesRepo<Key, Value>( class MapKeyValuesRepo<Key, Value>(
private val map: MutableMap<Key, MutableList<Value>> = mutableMapOf() private val map: MutableMap<Key, MutableList<Value>> = mutableMapOf(),
private val locker: SmartRWLocker = SmartRWLocker()
) : KeyValuesRepo<Key, Value>, ) : KeyValuesRepo<Key, Value>,
ReadKeyValuesRepo<Key, Value> by MapReadKeyValuesRepo(map), ReadKeyValuesRepo<Key, Value> by MapReadKeyValuesRepo(map, locker),
WriteKeyValuesRepo<Key, Value> by MapWriteKeyValuesRepo(map) WriteKeyValuesRepo<Key, Value> by MapWriteKeyValuesRepo(map, locker)
fun <K, V> MutableMap<K, List<V>>.asKeyValuesRepo(): KeyValuesRepo<K, V> = MapKeyValuesRepo( /**
map { (k, v) -> k to v.toMutableList() }.toMap().toMutableMap() * [MutableMap]-based [KeyValuesRepo]. All internal operations will be locked with [locker]
*
* **Warning**: In case you are using your own [MutableMap] as [this] receiver you should be careful with operations on [this] map
*/
fun <K, V> MutableMap<K, List<V>>.asKeyValuesRepo(locker: SmartRWLocker = SmartRWLocker()): KeyValuesRepo<K, V> = MapKeyValuesRepo(
map { (k, v) -> k to v.toMutableList() }.toMap().toMutableMap(),
locker
) )