mirror of
				https://github.com/InsanusMokrassar/MicroUtils.git
				synced 2025-10-30 19:50:31 +00:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 605fc3cff9 | |||
| 12cd6f48f8 | |||
| f09d92be32 | 
| @@ -1,5 +1,11 @@ | |||||||
| # Changelog | # Changelog | ||||||
|  |  | ||||||
|  | ## 0.22.9 | ||||||
|  |  | ||||||
|  | * `Repos`: | ||||||
|  |   * `Cache`: | ||||||
|  |     * Add direct caching repos | ||||||
|  |  | ||||||
| ## 0.22.8 | ## 0.22.8 | ||||||
|  |  | ||||||
| * `Common`: | * `Common`: | ||||||
|   | |||||||
| @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 | |||||||
| # Project data | # Project data | ||||||
|  |  | ||||||
| group=dev.inmo | group=dev.inmo | ||||||
| version=0.22.8 | version=0.22.9 | ||||||
| android_code_version=274 | android_code_version=275 | ||||||
|   | |||||||
| @@ -0,0 +1,113 @@ | |||||||
|  | package dev.inmo.micro_utils.repos.cache.full.direct | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.common.* | ||||||
|  | import dev.inmo.micro_utils.coroutines.SmartRWLocker | ||||||
|  | import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions | ||||||
|  | import dev.inmo.micro_utils.coroutines.withReadAcquire | ||||||
|  | import dev.inmo.micro_utils.coroutines.withWriteLock | ||||||
|  | import dev.inmo.micro_utils.pagination.Pagination | ||||||
|  | import dev.inmo.micro_utils.pagination.PaginationResult | ||||||
|  | import dev.inmo.micro_utils.repos.* | ||||||
|  | import dev.inmo.micro_utils.repos.cache.* | ||||||
|  | import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode | ||||||
|  | import dev.inmo.micro_utils.repos.cache.util.actualizeAll | ||||||
|  | import kotlinx.coroutines.CoroutineScope | ||||||
|  | import kotlinx.coroutines.Dispatchers | ||||||
|  |  | ||||||
|  | open class DirectFullReadCRUDCacheRepo<ObjectType, IdType>( | ||||||
|  |     protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>, | ||||||
|  |     protected open val kvCache: KeyValueRepo<IdType, ObjectType>, | ||||||
|  |     protected val locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  |     protected open val idGetter: (ObjectType) -> IdType | ||||||
|  | ) : ReadCRUDRepo<ObjectType, IdType>, DirectFullCacheRepo { | ||||||
|  |     protected open suspend fun actualizeAll() { | ||||||
|  |         kvCache.actualizeAll(parentRepo, locker = locker) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = locker.withReadAcquire { | ||||||
|  |         kvCache.values(pagination) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun getIdsByPagination(pagination: Pagination): PaginationResult<IdType> = locker.withReadAcquire { | ||||||
|  |         kvCache.keys(pagination) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun count(): Long = locker.withReadAcquire { | ||||||
|  |         kvCache.count() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun contains(id: IdType): Boolean = locker.withReadAcquire { | ||||||
|  |         kvCache.contains(id) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun getAll(): Map<IdType, ObjectType> = locker.withReadAcquire { | ||||||
|  |         kvCache.getAll() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun getById(id: IdType): ObjectType? = locker.withReadAcquire { | ||||||
|  |         kvCache.get(id) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun invalidate() { | ||||||
|  |         actualizeAll() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.directlyCached( | ||||||
|  |     kvCache: KeyValueRepo<IdType, ObjectType>, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  |     idGetter: (ObjectType) -> IdType | ||||||
|  | ) = DirectFullReadCRUDCacheRepo(this, kvCache, locker, idGetter) | ||||||
|  |  | ||||||
|  | open class DirectFullCRUDCacheRepo<ObjectType, IdType, InputValueType>( | ||||||
|  |     override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>, | ||||||
|  |     kvCache: KeyValueRepo<IdType, ObjectType>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     skipStartInvalidate: Boolean = false, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate), | ||||||
|  |     idGetter: (ObjectType) -> IdType | ||||||
|  | ) : DirectFullReadCRUDCacheRepo<ObjectType, IdType>( | ||||||
|  |     parentRepo, | ||||||
|  |     kvCache, | ||||||
|  |     locker, | ||||||
|  |     idGetter | ||||||
|  | ), | ||||||
|  |     WriteCRUDRepo<ObjectType, IdType, InputValueType> by WriteCRUDCacheRepo( | ||||||
|  |         parentRepo, | ||||||
|  |         kvCache, | ||||||
|  |         scope, | ||||||
|  |         locker, | ||||||
|  |         idGetter | ||||||
|  |     ), | ||||||
|  |     CRUDRepo<ObjectType, IdType, InputValueType> { | ||||||
|  |     init { | ||||||
|  |         if (!skipStartInvalidate) { | ||||||
|  |             scope.launchSafelyWithoutExceptions { | ||||||
|  |                 if (locker.writeMutex.isLocked) { | ||||||
|  |                     initialInvalidate() | ||||||
|  |                 } else { | ||||||
|  |                     invalidate() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected open suspend fun initialInvalidate() { | ||||||
|  |         try { | ||||||
|  |             kvCache.actualizeAll(parentRepo, locker = null) | ||||||
|  |         } finally { | ||||||
|  |             locker.unlockWrite() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     override suspend fun invalidate() { | ||||||
|  |         actualizeAll() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <ObjectType, IdType, InputType> CRUDRepo<ObjectType, IdType, InputType>.directFullyCached( | ||||||
|  |     kvCache: KeyValueRepo<IdType, ObjectType> = MapKeyValueRepo(), | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     skipStartInvalidate: Boolean = false, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  |     idGetter: (ObjectType) -> IdType | ||||||
|  | ) = DirectFullCRUDCacheRepo(this, kvCache, scope, skipStartInvalidate, locker, idGetter) | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | package dev.inmo.micro_utils.repos.cache.full.direct | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.repos.cache.full.FullCacheRepo | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Repos-inheritors MUST realize their methods via next logic: | ||||||
|  |  * | ||||||
|  |  * * Reloading of data in cache must be reactive (e.g. via Flow) or direct mutation methods usage (override set and | ||||||
|  |  * mutate cache inside, for example) | ||||||
|  |  * * All reading methods must take data from cache via synchronization with [dev.inmo.micro_utils.coroutines.SmartRWLocker] | ||||||
|  |  */ | ||||||
|  | interface DirectFullCacheRepo : FullCacheRepo { | ||||||
|  | } | ||||||
| @@ -0,0 +1,177 @@ | |||||||
|  | package dev.inmo.micro_utils.repos.cache.full.direct | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.coroutines.SmartRWLocker | ||||||
|  | import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions | ||||||
|  | import dev.inmo.micro_utils.coroutines.withReadAcquire | ||||||
|  | import dev.inmo.micro_utils.coroutines.withWriteLock | ||||||
|  | import dev.inmo.micro_utils.pagination.Pagination | ||||||
|  | import dev.inmo.micro_utils.pagination.PaginationResult | ||||||
|  | import dev.inmo.micro_utils.repos.* | ||||||
|  | import dev.inmo.micro_utils.repos.cache.full.FullKeyValueCacheRepo | ||||||
|  | import dev.inmo.micro_utils.repos.cache.full.FullReadKeyValueCacheRepo | ||||||
|  | import dev.inmo.micro_utils.repos.cache.full.FullWriteKeyValueCacheRepo | ||||||
|  | import dev.inmo.micro_utils.repos.cache.util.actualizeAll | ||||||
|  | import kotlinx.coroutines.CoroutineScope | ||||||
|  | import kotlinx.coroutines.Dispatchers | ||||||
|  | import kotlinx.coroutines.flow.Flow | ||||||
|  | import kotlinx.coroutines.flow.launchIn | ||||||
|  | import kotlinx.coroutines.flow.onEach | ||||||
|  |  | ||||||
|  | open class DirectFullReadKeyValueCacheRepo<Key, Value>( | ||||||
|  |     protected open val parentRepo: ReadKeyValueRepo<Key, Value>, | ||||||
|  |     protected open val kvCache: KeyValueRepo<Key, Value>, | ||||||
|  |     protected val locker: SmartRWLocker = SmartRWLocker() | ||||||
|  | ) : DirectFullCacheRepo, ReadKeyValueRepo<Key, Value> { | ||||||
|  |     protected open suspend fun actualizeAll() { | ||||||
|  |         kvCache.actualizeAll(parentRepo, locker) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun get(k: Key): Value? = locker.withReadAcquire { | ||||||
|  |         kvCache.get(k) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = locker.withReadAcquire { | ||||||
|  |         kvCache.values(pagination, reversed) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun count(): Long = locker.withReadAcquire { | ||||||
|  |         kvCache.count() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun contains(key: Key): Boolean = locker.withReadAcquire { | ||||||
|  |         kvCache.contains(key) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun getAll(): Map<Key, Value> = locker.withReadAcquire { | ||||||
|  |         kvCache.getAll() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = locker.withReadAcquire { | ||||||
|  |         kvCache.keys(pagination, reversed) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = locker.withReadAcquire { | ||||||
|  |         kvCache.keys(v, pagination, reversed) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun invalidate() { | ||||||
|  |         actualizeAll() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <Key, Value> ReadKeyValueRepo<Key, Value>.directlyCached( | ||||||
|  |     kvCache: KeyValueRepo<Key, Value>, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker() | ||||||
|  | ) = DirectFullReadKeyValueCacheRepo(this, kvCache, locker) | ||||||
|  |  | ||||||
|  | open class DirectFullWriteKeyValueCacheRepo<Key, Value>( | ||||||
|  |     protected open val parentRepo: WriteKeyValueRepo<Key, Value>, | ||||||
|  |     protected open val kvCache: KeyValueRepo<Key, Value>, | ||||||
|  |     protected val locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  | ) : DirectFullCacheRepo, WriteKeyValueRepo<Key, Value> by parentRepo { | ||||||
|  |     override val onNewValue: Flow<Pair<Key, Value>> | ||||||
|  |         get() = parentRepo.onNewValue | ||||||
|  |     override val onValueRemoved: Flow<Key> | ||||||
|  |         get() = parentRepo.onValueRemoved | ||||||
|  |  | ||||||
|  |     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() { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             kvCache.clear() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <Key, Value> WriteKeyValueRepo<Key, Value>.directlyCached( | ||||||
|  |     kvCache: KeyValueRepo<Key, Value>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default) | ||||||
|  | ) = DirectFullWriteKeyValueCacheRepo(this, kvCache, scope = scope) | ||||||
|  |  | ||||||
|  | open class DirectFullKeyValueCacheRepo<Key, Value>( | ||||||
|  |     override val parentRepo: KeyValueRepo<Key, Value>, | ||||||
|  |     kvCache: KeyValueRepo<Key, Value>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     skipStartInvalidate: Boolean = false, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate), | ||||||
|  | ) : DirectFullCacheRepo, | ||||||
|  |     KeyValueRepo<Key, Value> , | ||||||
|  |     WriteKeyValueRepo<Key, Value> by DirectFullWriteKeyValueCacheRepo( | ||||||
|  |         parentRepo, | ||||||
|  |         kvCache, | ||||||
|  |         locker, | ||||||
|  |         scope | ||||||
|  |     ), | ||||||
|  |     DirectFullReadKeyValueCacheRepo<Key, Value>(parentRepo, kvCache, locker) { | ||||||
|  |     init { | ||||||
|  |         if (!skipStartInvalidate) { | ||||||
|  |             scope.launchSafelyWithoutExceptions { | ||||||
|  |                 if (locker.writeMutex.isLocked) { | ||||||
|  |                     initialInvalidate() | ||||||
|  |                 } else { | ||||||
|  |                     invalidate() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     protected open suspend fun initialInvalidate() { | ||||||
|  |         try { | ||||||
|  |             kvCache.actualizeAll(parentRepo, locker = null) | ||||||
|  |         } finally { | ||||||
|  |             locker.unlockWrite() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     override suspend fun invalidate() { | ||||||
|  |         kvCache.actualizeAll(parentRepo, locker) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun clear() { | ||||||
|  |         parentRepo.clear() | ||||||
|  |         kvCache.clear() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun unsetWithValues(toUnset: List<Value>) = parentRepo.unsetWithValues(toUnset) | ||||||
|  |  | ||||||
|  |     override suspend fun set(toSet: Map<Key, Value>) { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             parentRepo.set(toSet) | ||||||
|  |             kvCache.set( | ||||||
|  |                 toSet.filter { | ||||||
|  |                     parentRepo.contains(it.key) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun unset(toUnset: List<Key>) { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             parentRepo.unset(toUnset) | ||||||
|  |             kvCache.unset( | ||||||
|  |                 toUnset.filter { | ||||||
|  |                     !parentRepo.contains(it) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <Key, Value> KeyValueRepo<Key, Value>.directlyFullyCached( | ||||||
|  |     kvCache: KeyValueRepo<Key, Value> = MapKeyValueRepo(), | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     skipStartInvalidate: Boolean = false, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker() | ||||||
|  | ) = DirectFullKeyValueCacheRepo(this, kvCache, scope, skipStartInvalidate, locker) | ||||||
| @@ -0,0 +1,243 @@ | |||||||
|  | package dev.inmo.micro_utils.repos.cache.full.direct | ||||||
|  |  | ||||||
|  | import dev.inmo.micro_utils.common.* | ||||||
|  | import dev.inmo.micro_utils.coroutines.SmartRWLocker | ||||||
|  | import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions | ||||||
|  | import dev.inmo.micro_utils.coroutines.withReadAcquire | ||||||
|  | import dev.inmo.micro_utils.coroutines.withWriteLock | ||||||
|  | import dev.inmo.micro_utils.pagination.* | ||||||
|  | import dev.inmo.micro_utils.pagination.utils.* | ||||||
|  | import dev.inmo.micro_utils.repos.* | ||||||
|  | import dev.inmo.micro_utils.repos.cache.util.ActualizeAllClearMode | ||||||
|  | import dev.inmo.micro_utils.repos.cache.util.actualizeAll | ||||||
|  | import kotlinx.coroutines.CoroutineScope | ||||||
|  | import kotlinx.coroutines.Dispatchers | ||||||
|  | import kotlinx.coroutines.flow.* | ||||||
|  |  | ||||||
|  | open class DirectFullReadKeyValuesCacheRepo<Key,Value>( | ||||||
|  |     protected open val parentRepo: ReadKeyValuesRepo<Key, Value>, | ||||||
|  |     protected open val kvCache: KeyValueRepo<Key, List<Value>>, | ||||||
|  |     protected val locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  | ) : ReadKeyValuesRepo<Key, Value>, DirectFullCacheRepo { | ||||||
|  |     protected open suspend fun actualizeKey(k: Key) { | ||||||
|  |         kvCache.actualizeAll(locker = locker, clearMode = ActualizeAllClearMode.Never) { | ||||||
|  |             mapOf(k to parentRepo.getAll(k)) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected open suspend fun actualizeAll() { | ||||||
|  |         kvCache.actualizeAll(parentRepo, locker = locker) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> { | ||||||
|  |         return locker.withReadAcquire { | ||||||
|  |             kvCache.get(k) ?.paginate( | ||||||
|  |                 pagination.let { if (reversed) it.reverse(count(k)) else it } | ||||||
|  |             ) ?.let { | ||||||
|  |                 if (reversed) it.copy(results = it.results.reversed()) else it | ||||||
|  |             } | ||||||
|  |         } ?: emptyPaginationResult() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun getAll(k: Key, reversed: Boolean): List<Value> { | ||||||
|  |         return locker.withReadAcquire { | ||||||
|  |             kvCache.get(k) ?.optionallyReverse(reversed) | ||||||
|  |         } ?: emptyList() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun getAll(reverseLists: Boolean): Map<Key, List<Value>> { | ||||||
|  |         return locker.withReadAcquire { | ||||||
|  |             kvCache.getAll().takeIf { it.isNotEmpty() } ?.let { | ||||||
|  |                 if (reverseLists) { | ||||||
|  |                     it.mapValues { it.value.reversed() } | ||||||
|  |                 } else { | ||||||
|  |                     it | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } ?: emptyMap() | ||||||
|  |     } | ||||||
|  |     override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> { | ||||||
|  |         return locker.withReadAcquire { | ||||||
|  |             kvCache.keys(pagination, reversed).takeIf { it.results.isNotEmpty() } | ||||||
|  |         } ?: emptyPaginationResult() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun count(): Long = locker.withReadAcquire { kvCache.count() } | ||||||
|  |  | ||||||
|  |     override suspend fun count(k: Key): Long = locker.withReadAcquire { kvCache.get(k) ?.size } ?.toLong() ?: 0L | ||||||
|  |  | ||||||
|  |     override suspend fun contains(k: Key, v: Value): Boolean = locker.withReadAcquire { kvCache.get(k) ?.contains(v) } == true | ||||||
|  |  | ||||||
|  |     override suspend fun contains(k: Key): Boolean = locker.withReadAcquire { kvCache.contains(k) } | ||||||
|  |  | ||||||
|  |     override suspend fun keys( | ||||||
|  |         v: Value, | ||||||
|  |         pagination: Pagination, | ||||||
|  |         reversed: Boolean | ||||||
|  |     ): PaginationResult<Key> { | ||||||
|  |         val keys = locker.withReadAcquire { | ||||||
|  |             getAllWithNextPaging { kvCache.keys(it) }.filter { kvCache.get(it)?.contains(v) == true } | ||||||
|  |                 .optionallyReverse(reversed) | ||||||
|  |         } | ||||||
|  |         val result = if (keys.isNotEmpty()) { | ||||||
|  |             keys.paginate(pagination.optionallyReverse(keys.size, reversed)).takeIf { it.results.isNotEmpty() } | ||||||
|  |         } else { | ||||||
|  |             null | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result ?: emptyPaginationResult() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun invalidate() { | ||||||
|  |         actualizeAll() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <Key, Value> ReadKeyValuesRepo<Key, Value>.directlyCached( | ||||||
|  |     kvCache: KeyValueRepo<Key, List<Value>>, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  | ) = DirectFullReadKeyValuesCacheRepo(this, kvCache, locker) | ||||||
|  |  | ||||||
|  | open class DirectFullWriteKeyValuesCacheRepo<Key,Value>( | ||||||
|  |     parentRepo: WriteKeyValuesRepo<Key, Value>, | ||||||
|  |     protected open val kvCache: KeyValueRepo<Key, List<Value>>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     protected val locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  | ) : WriteKeyValuesRepo<Key, Value> by parentRepo, DirectFullCacheRepo { | ||||||
|  |     protected val onNewJob = parentRepo.onNewValue.onEach { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             kvCache.set( | ||||||
|  |                 it.first, | ||||||
|  |                 kvCache.get(it.first) ?.plus(it.second) ?: listOf(it.second) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     }.launchIn(scope) | ||||||
|  |     protected val onRemoveJob = parentRepo.onValueRemoved.onEach { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             kvCache.set( | ||||||
|  |                 it.first, | ||||||
|  |                 kvCache.get(it.first)?.minus(it.second) ?: return@onEach | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     }.launchIn(scope) | ||||||
|  |  | ||||||
|  |     override suspend fun invalidate() { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             kvCache.clear() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <Key, Value> WriteKeyValuesRepo<Key, Value>.directlyCached( | ||||||
|  |     kvCache: KeyValueRepo<Key, List<Value>>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  | ) = DirectFullWriteKeyValuesCacheRepo(this, kvCache, scope, locker) | ||||||
|  |  | ||||||
|  | open class DirectFullKeyValuesCacheRepo<Key,Value>( | ||||||
|  |     override val parentRepo: KeyValuesRepo<Key, Value>, | ||||||
|  |     kvCache: KeyValueRepo<Key, List<Value>>, | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     skipStartInvalidate: Boolean = false, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate), | ||||||
|  | ) : KeyValuesRepo<Key, Value>, | ||||||
|  |     DirectFullReadKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, locker), | ||||||
|  |     WriteKeyValuesRepo<Key, Value> by parentRepo { | ||||||
|  |     init { | ||||||
|  |         if (!skipStartInvalidate) { | ||||||
|  |             scope.launchSafelyWithoutExceptions { | ||||||
|  |                 if (locker.writeMutex.isLocked) { | ||||||
|  |                     initialInvalidate() | ||||||
|  |                 } else { | ||||||
|  |                     invalidate() | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun clearWithValue(v: Value) { | ||||||
|  |         doAllWithCurrentPaging { | ||||||
|  |             keys(v, it).also { | ||||||
|  |                 remove(it.results.associateWith { listOf(v) }) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected open suspend fun initialInvalidate() { | ||||||
|  |         try { | ||||||
|  |             kvCache.actualizeAll(parentRepo, locker = null) | ||||||
|  |         } finally { | ||||||
|  |             locker.unlockWrite() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     override suspend fun invalidate() { | ||||||
|  |         kvCache.actualizeAll(parentRepo, locker = locker) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun set(toSet: Map<Key, List<Value>>) { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             parentRepo.set(toSet) | ||||||
|  |             kvCache.set( | ||||||
|  |                 toSet.filter { | ||||||
|  |                     parentRepo.contains(it.key) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun add(toAdd: Map<Key, List<Value>>) { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             parentRepo.add(toAdd) | ||||||
|  |             toAdd.forEach { | ||||||
|  |                 val filtered = it.value.filter { v -> | ||||||
|  |                     parentRepo.contains(it.key, v) | ||||||
|  |                 }.ifEmpty { | ||||||
|  |                     return@forEach | ||||||
|  |                 } | ||||||
|  |                 kvCache.set( | ||||||
|  |                     it.key, | ||||||
|  |                     (kvCache.get(it.key) ?: emptyList()) + filtered | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun remove(toRemove: Map<Key, List<Value>>) { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             parentRepo.remove(toRemove) | ||||||
|  |             toRemove.forEach { | ||||||
|  |                 val filtered = it.value.filter { v -> | ||||||
|  |                     !parentRepo.contains(it.key, v) | ||||||
|  |                 }.ifEmpty { | ||||||
|  |                     return@forEach | ||||||
|  |                 }.toSet() | ||||||
|  |                 val resultList = (kvCache.get(it.key) ?: emptyList()) - filtered | ||||||
|  |                 if (resultList.isEmpty()) { | ||||||
|  |                     kvCache.unset(it.key) | ||||||
|  |                 } else { | ||||||
|  |                     kvCache.set( | ||||||
|  |                         it.key, | ||||||
|  |                         resultList | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override suspend fun clear(k: Key) { | ||||||
|  |         locker.withWriteLock { | ||||||
|  |             parentRepo.clear(k) | ||||||
|  |             if (parentRepo.contains(k)) { | ||||||
|  |                 return@withWriteLock | ||||||
|  |             } | ||||||
|  |             kvCache.unset(k) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fun <Key, Value> KeyValuesRepo<Key, Value>.directlyFullyCached( | ||||||
|  |     kvCache: KeyValueRepo<Key, List<Value>> = MapKeyValueRepo(), | ||||||
|  |     scope: CoroutineScope = CoroutineScope(Dispatchers.Default), | ||||||
|  |     skipStartInvalidate: Boolean = false, | ||||||
|  |     locker: SmartRWLocker = SmartRWLocker(), | ||||||
|  | ) = DirectFullKeyValuesCacheRepo(this, kvCache, scope, skipStartInvalidate, locker) | ||||||
		Reference in New Issue
	
	Block a user