All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.dev.inmo.micro_utils.repos.cache.CRUDCacheRepo.kt Maven / Gradle / Ivy

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.cache.cache.KVCache
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 ReadCRUDCacheRepo(
    protected open val parentRepo: ReadCRUDRepo,
    protected open val kvCache: KVCache,
    protected val locker: SmartRWLocker = SmartRWLocker(),
    protected open val idGetter: (ObjectType) -> IdType
) : ReadCRUDRepo by parentRepo, CommonCacheRepo {
    override suspend fun getById(id: IdType): ObjectType? = locker.withReadAcquire {
        kvCache.get(id)
    } ?: (parentRepo.getById(id) ?.also {
        locker.withWriteLock {
            kvCache.set(id, it)
        }
    })

    override suspend fun getAll(): Map {
        return locker.withReadAcquire {
            kvCache.getAll()
        }.takeIf { it.size.toLong() == count() } ?: parentRepo.getAll().also {
            locker.withWriteLock {
                kvCache.actualizeAll(clearMode = ActualizeAllClearMode.BeforeSet) { it }
            }
        }
    }

    override suspend fun contains(id: IdType): Boolean = locker.withReadAcquire {
        kvCache.contains(id)
    } || parentRepo.contains(id)

    override suspend fun invalidate() = locker.withWriteLock {
        kvCache.clear()
    }
}

fun  ReadCRUDRepo.cached(
    kvCache: KVCache,
    locker: SmartRWLocker = SmartRWLocker(),
    idGetter: (ObjectType) -> IdType
) = ReadCRUDCacheRepo(this, kvCache, locker, idGetter)

open class WriteCRUDCacheRepo(
    protected open val parentRepo: WriteCRUDRepo,
    protected open val kvCache: KeyValueRepo,
    protected open val scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
    protected val locker: SmartRWLocker = SmartRWLocker(),
    protected open val idGetter: (ObjectType) -> IdType
) : WriteCRUDRepo, CommonCacheRepo {
    override val newObjectsFlow: Flow by parentRepo::newObjectsFlow
    override val updatedObjectsFlow: Flow by parentRepo::updatedObjectsFlow
    override val deletedObjectsIdsFlow: Flow by parentRepo::deletedObjectsIdsFlow

    val createdObjectsFlowJob = parentRepo.newObjectsFlow.onEach {
        locker.withWriteLock {
            kvCache.set(idGetter(it), it)
        }
    }.launchIn(scope)

    val updatedObjectsFlowJob = parentRepo.updatedObjectsFlow.onEach {
        locker.withWriteLock {
            kvCache.set(idGetter(it), it)
        }
    }.launchIn(scope)

    val deletedObjectsFlowJob = parentRepo.deletedObjectsIdsFlow.onEach {
        locker.withWriteLock {
            kvCache.unset(it)
        }
    }.launchIn(scope)

    override suspend fun deleteById(ids: List) = parentRepo.deleteById(ids).also {
        locker.withWriteLock {
            kvCache.unset(ids)
        }
    }

    override suspend fun update(values: List>): List {
        val updated = parentRepo.update(values)

        locker.withWriteLock {
            kvCache.unset(values.map { it.id })
            kvCache.set(updated.associateBy { idGetter(it) })
        }

        return updated
    }

    override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
        return parentRepo.update(id, value) ?.also {
            locker.withWriteLock {
                kvCache.unset(id)
                kvCache.set(idGetter(it), it)
            }
        }
    }

    override suspend fun create(values: List): List {
        val created = parentRepo.create(values)

        locker.withWriteLock {
            kvCache.set(
                created.associateBy { idGetter(it) }
            )
        }

        return created
    }

    override suspend fun invalidate() = locker.withWriteLock {
        kvCache.clear()
    }
}

fun  WriteCRUDRepo.caching(
    kvCache: KVCache,
    scope: CoroutineScope,
    locker: SmartRWLocker = SmartRWLocker(),
    idGetter: (ObjectType) -> IdType
) = WriteCRUDCacheRepo(this, kvCache, scope, locker, idGetter)


open class CRUDCacheRepo(
    override val parentRepo: CRUDRepo,
    kvCache: KVCache,
    scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
    locker: SmartRWLocker = SmartRWLocker(),
    idGetter: (ObjectType) -> IdType
) : ReadCRUDCacheRepo(
    parentRepo,
    kvCache,
    locker,
    idGetter
),
    WriteCRUDRepo by WriteCRUDCacheRepo(
    parentRepo,
    kvCache,
    scope,
    locker,
    idGetter
),
    CRUDRepo {
    override suspend fun invalidate() = kvCache.actualizeAll(parentRepo, locker = locker)
}

fun  CRUDRepo.cached(
    kvCache: KVCache,
    scope: CoroutineScope,
    locker: SmartRWLocker = SmartRWLocker(),
    idGetter: (ObjectType) -> IdType
) = CRUDCacheRepo(this, kvCache, scope, locker, idGetter)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy