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