mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-26 20:18:48 +00:00
commit
e775b58d41
@ -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`:
|
||||||
|
@ -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
|
||||||
|
@ -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]
|
||||||
|
|
||||||
|
@ -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.get(id)
|
||||||
|
} ?: (parentRepo.getById(id) ?.also {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.set(id, it)
|
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.getAll()
|
||||||
|
}.takeIf { it.size.toLong() == count() } ?: parentRepo.getAll().also {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.actualizeAll(true) { it }
|
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)
|
||||||
|
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.unset(values.map { it.id })
|
kvCache.unset(values.map { it.id })
|
||||||
kvCache.set(updated.associateBy { idGetter(it) })
|
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 {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.unset(id)
|
kvCache.unset(id)
|
||||||
kvCache.set(idGetter(it), it)
|
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)
|
||||||
|
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.set(
|
kvCache.set(
|
||||||
created.associateBy { idGetter(it) }
|
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)
|
||||||
|
@ -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,12 +13,22 @@ 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 {
|
||||||
|
keys(pagination, reversed).let {
|
||||||
it.changeResultsUnchecked(
|
it.changeResultsUnchecked(
|
||||||
it.results.mapNotNull {
|
it.results.mapNotNull {
|
||||||
get(it)
|
get(it)
|
||||||
@ -23,37 +36,59 @@ open class ReadKeyValueCacheRepo<Key,Value>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getAll(): Map<Key, Value> = kvCache.getAll().takeIf {
|
|
||||||
it.size.toLong() == count()
|
|
||||||
} ?: parentRepo.getAll().also {
|
|
||||||
kvCache.set(it)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun invalidate() = kvCache.clear()
|
override suspend fun getAll(): Map<Key, Value> = locker.withReadAcquire {
|
||||||
|
kvCache.getAll()
|
||||||
|
}.takeIf {
|
||||||
|
it.size.toLong() == count()
|
||||||
|
} ?: parentRepo.getAll().also {
|
||||||
|
locker.withWriteLock {
|
||||||
|
kvCache.set(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.clear()
|
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)
|
||||||
|
@ -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 {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.set(k, it)
|
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) {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.unset(k) // clear as invalid
|
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) ->
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.set(
|
kvCache.set(
|
||||||
k,
|
k,
|
||||||
kvCache.get(k) ?.plus(v) ?: return@onEach
|
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) ->
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.set(
|
kvCache.set(
|
||||||
k,
|
k,
|
||||||
kvCache.get(k) ?.minus(v) ?: return@onEach
|
kvCache.get(k)?.minus(v) ?: return@onEach
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}.launchIn(scope)
|
}.launchIn(scope)
|
||||||
protected val onDataClearedJob = parentRepo.onDataCleared.onEach {
|
protected val onDataClearedJob = parentRepo.onDataCleared.onEach {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.unset(it)
|
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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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,26 +16,27 @@ 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() {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.clear()
|
kvCache.clear()
|
||||||
kvCache.set(parentRepo.getAll { keys(it) }.toMap())
|
kvCache.set(parentRepo.getAll { keys(it) }.toMap())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun get(k: Key): Value? = doOrTakeAndActualize(
|
override suspend fun get(k: Key): Value? = doOrTakeAndActualize(
|
||||||
{ get(k) ?.optional ?: Optional.absent() },
|
{ get(k) ?.optional ?: Optional.absent() },
|
||||||
@ -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() {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.clear()
|
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,8 +141,10 @@ 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() {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.actualizeAll(parentRepo)
|
kvCache.actualizeAll(parentRepo)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun clear() {
|
override suspend fun clear() {
|
||||||
parentRepo.clear()
|
parentRepo.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)
|
||||||
|
@ -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,30 +15,33 @@ 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) {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.set(k, parentRepo.getAll(k))
|
kvCache.set(k, parentRepo.getAll(k))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected open suspend fun actualizeAll() {
|
protected open suspend fun actualizeAll() {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.actualizeAll(parentRepo)
|
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> {
|
||||||
return doOrTakeAndActualize(
|
return doOrTakeAndActualize(
|
||||||
@ -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 {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.set(
|
kvCache.set(
|
||||||
it.first,
|
it.first,
|
||||||
kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second)
|
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 {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.set(
|
kvCache.set(
|
||||||
it.first,
|
it.first,
|
||||||
kvCache.get(it.first) ?.minus(it.second) ?: return@onEach
|
kvCache.get(it.first)?.minus(it.second) ?: return@onEach
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}.launchIn(scope)
|
}.launchIn(scope)
|
||||||
|
|
||||||
override suspend fun invalidate() {
|
override suspend fun invalidate() {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.clear()
|
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,8 +178,10 @@ open class FullKeyValuesCacheRepo<Key,Value>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun invalidate() {
|
override suspend fun invalidate() {
|
||||||
|
locker.withWriteLock {
|
||||||
kvCache.actualizeAll(parentRepo)
|
kvCache.actualizeAll(parentRepo)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun removeWithValue(v: Value) {
|
override suspend fun removeWithValue(v: Value) {
|
||||||
super<FullWriteKeyValuesCacheRepo>.removeWithValue(v)
|
super<FullWriteKeyValuesCacheRepo>.removeWithValue(v)
|
||||||
@ -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)
|
||||||
|
@ -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,
|
||||||
) {
|
) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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.keys.drop(pagination.firstIndex).take(pagination.size).mapNotNull {
|
||||||
map[it]
|
map[it]
|
||||||
}.createPaginationResult(
|
}.createPaginationResult(
|
||||||
pagination,
|
pagination,
|
||||||
count()
|
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 {
|
||||||
|
map.keys.drop(pagination.firstIndex).take(pagination.size).createPaginationResult(
|
||||||
pagination,
|
pagination,
|
||||||
count()
|
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,7 +76,8 @@ 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 {
|
||||||
|
values.map {
|
||||||
val (id, newObject) = createObject(it)
|
val (id, newObject) = createObject(it)
|
||||||
map[id] = newObject
|
map[id] = newObject
|
||||||
newObject.also { _ ->
|
newObject.also { _ ->
|
||||||
@ -53,39 +85,58 @@ abstract class WriteMapCRUDRepo<ObjectType, IdType, InputValueType>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
|
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
|
||||||
return values.mapNotNull { update(it.first, it.second) }
|
return values.mapNotNull { update(it.first, it.second) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteById(ids: List<IdType>) {
|
override suspend fun deleteById(ids: List<IdType>) {
|
||||||
|
locker.withWriteLock {
|
||||||
ids.forEach {
|
ids.forEach {
|
||||||
map.remove(it) ?.also { _ -> _deletedObjectsIdsFlow.emit(it) }
|
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)
|
||||||
|
@ -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,18 +10,29 @@ 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> {
|
||||||
|
return locker.withReadAcquire {
|
||||||
val values = map.values
|
val values = map.values
|
||||||
val actualPagination = if (reversed) pagination.reverse(values.size) else pagination
|
val actualPagination = if (reversed) pagination.reverse(values.size) else pagination
|
||||||
return values.paginate(actualPagination).let {
|
values.paginate(actualPagination).let {
|
||||||
if (reversed) {
|
if (reversed) {
|
||||||
it.copy(results = it.results.reversed())
|
it.copy(results = it.results.reversed())
|
||||||
} else {
|
} else {
|
||||||
@ -26,11 +40,13 @@ class ReadMapKeyValueRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> {
|
||||||
|
return locker.withReadAcquire {
|
||||||
val keys = map.keys
|
val keys = 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 {
|
keys.paginate(actualPagination).let {
|
||||||
if (reversed) {
|
if (reversed) {
|
||||||
it.copy(results = it.results.reversed())
|
it.copy(results = it.results.reversed())
|
||||||
} else {
|
} else {
|
||||||
@ -38,11 +54,13 @@ class ReadMapKeyValueRepo<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> {
|
||||||
|
return locker.withReadAcquire {
|
||||||
val keys: List<Key> = map.mapNotNull { (k, value) -> if (v == value) k else null }
|
val keys: List<Key> = map.mapNotNull { (k, value) -> if (v == value) k else null }
|
||||||
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 {
|
keys.paginate(actualPagination).let {
|
||||||
if (reversed) {
|
if (reversed) {
|
||||||
it.copy(results = it.results.reversed())
|
it.copy(results = it.results.reversed())
|
||||||
} else {
|
} else {
|
||||||
@ -50,16 +68,25 @@ class ReadMapKeyValueRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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,19 +94,23 @@ 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>) {
|
||||||
|
locker.withWriteLock {
|
||||||
toUnset.forEach { k ->
|
toUnset.forEach { k ->
|
||||||
map.remove(k) ?.also { _ -> _onValueRemoved.emit(k) }
|
map.remove(k) ?.also { _ -> _onValueRemoved.emit(k) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun unsetWithValues(toUnset: List<Value>) {
|
override suspend fun unsetWithValues(toUnset: List<Value>) {
|
||||||
|
locker.withWriteLock {
|
||||||
map.mapNotNull { (k, v) ->
|
map.mapNotNull { (k, v) ->
|
||||||
k.takeIf { v in toUnset }
|
k.takeIf { v in toUnset }
|
||||||
}.forEach {
|
}.forEach {
|
||||||
@ -87,17 +118,32 @@ class WriteMapKeyValueRepo<Key, Value>(
|
|||||||
_onValueRemoved.emit(it)
|
_onValueRemoved.emit(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
|
|
||||||
class MapKeyValueRepo<Key, Value>(
|
|
||||||
private val map: MutableMap<Key, Value> = mutableMapOf()
|
|
||||||
) : KeyValueRepo<Key, Value>,
|
|
||||||
ReadKeyValueRepo<Key, Value> by ReadMapKeyValueRepo(map),
|
|
||||||
WriteKeyValueRepo<Key, Value> by WriteMapKeyValueRepo(map) {
|
|
||||||
override suspend fun clear() {
|
|
||||||
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 [map] you should be careful with operations on this [map]
|
||||||
|
*/
|
||||||
|
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
|
||||||
|
class MapKeyValueRepo<Key, Value>(
|
||||||
|
private val map: MutableMap<Key, Value>,
|
||||||
|
private val locker: SmartRWLocker
|
||||||
|
) : KeyValueRepo<Key, Value>,
|
||||||
|
ReadKeyValueRepo<Key, Value> by ReadMapKeyValueRepo(map, locker),
|
||||||
|
WriteKeyValueRepo<Key, Value> by WriteMapKeyValueRepo(map, locker) {
|
||||||
|
constructor(map: MutableMap<Key, Value> = mutableMapOf()) : this(map, SmartRWLocker())
|
||||||
|
override suspend fun clear() {
|
||||||
|
locker.withWriteLock { map.clear() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [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)
|
||||||
|
@ -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,6 +89,7 @@ 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>>) {
|
||||||
|
locker.withWriteLock {
|
||||||
toAdd.keys.forEach { k ->
|
toAdd.keys.forEach { k ->
|
||||||
if (map.getOrPut(k) { mutableListOf() }.addAll(toAdd[k] ?: return@forEach)) {
|
if (map.getOrPut(k) { mutableListOf() }.addAll(toAdd[k] ?: return@forEach)) {
|
||||||
toAdd[k] ?.forEach { v ->
|
toAdd[k] ?.forEach { v ->
|
||||||
@ -72,53 +98,75 @@ class MapWriteKeyValuesRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||||
|
locker.withWriteLock {
|
||||||
toRemove.keys.forEach { k ->
|
toRemove.keys.forEach { k ->
|
||||||
if (map[k] ?.removeAll(toRemove[k] ?: return@forEach) == true) {
|
if (map[k]?.removeAll(toRemove[k] ?: return@forEach) == true) {
|
||||||
toRemove[k] ?.forEach { v ->
|
toRemove[k]?.forEach { v ->
|
||||||
_onValueRemoved.emit(k to v)
|
_onValueRemoved.emit(k to v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (map[k] ?.isEmpty() == true) {
|
if (map[k]?.isEmpty() == true) {
|
||||||
map.remove(k)
|
map.remove(k)
|
||||||
_onDataCleared.emit(k)
|
_onDataCleared.emit(k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun removeWithValue(v: Value) {
|
override suspend fun removeWithValue(v: Value) {
|
||||||
|
locker.withWriteLock {
|
||||||
map.forEach { (k, values) ->
|
map.forEach { (k, values) ->
|
||||||
if (values.remove(v)) {
|
if (values.remove(v)) {
|
||||||
_onValueRemoved.emit(k to v)
|
_onValueRemoved.emit(k to v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun clear(k: Key) {
|
override suspend fun clear(k: Key) {
|
||||||
|
locker.withWriteLock {
|
||||||
map.remove(k) ?.also { _onDataCleared.emit(k) }
|
map.remove(k) ?.also { _onDataCleared.emit(k) }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun clearWithValue(v: Value) {
|
override suspend fun clearWithValue(v: Value) {
|
||||||
|
locker.withWriteLock {
|
||||||
map.filter { (_, values) ->
|
map.filter { (_, values) ->
|
||||||
values.contains(v)
|
values.contains(v)
|
||||||
}.forEach {
|
}.forEach {
|
||||||
map.remove(it.key) ?.onEach { v ->
|
map.remove(it.key)?.onEach { v ->
|
||||||
_onValueRemoved.emit(it.key to v)
|
_onValueRemoved.emit(it.key to v)
|
||||||
} ?.also { _ ->
|
}?.also { _ ->
|
||||||
_onDataCleared.emit(it.key)
|
_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
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user