mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-10 02:34:06 +00:00
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,5 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.24.7
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `SQLite`: `3.49.0.0` -> `3.49.1.0`
|
||||||
|
* `Common`:
|
||||||
|
* Add `retryOnFailure` utility for simple retries code writing
|
||||||
|
* `Repos`:
|
||||||
|
* `Cache`:
|
||||||
|
* Fix of `FullKeyValueCacheRepo` fields usage
|
||||||
|
* `Exposed`:
|
||||||
|
* `AbstractExposedKeyValuesRepo` will produce `onValueRemoved` event on `set` if some data has been removed
|
||||||
|
|
||||||
## 0.24.6
|
## 0.24.6
|
||||||
|
|
||||||
* `Versions`:
|
* `Versions`:
|
||||||
|
@@ -0,0 +1,26 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will try to execute [action] and, if any exception will happen, execution will be retried.
|
||||||
|
* This process will happen at most [count] times. There is no any limits on [count] value, but [action] will run at
|
||||||
|
* least once and [retryOnFailure] will return its result if it is successful
|
||||||
|
*/
|
||||||
|
inline fun <T> retryOnFailure(count: Int, action: () -> T): T {
|
||||||
|
var triesCount = 0
|
||||||
|
while (true) {
|
||||||
|
val result = runCatching {
|
||||||
|
action()
|
||||||
|
}.onFailure {
|
||||||
|
triesCount++
|
||||||
|
|
||||||
|
if (triesCount >= count) {
|
||||||
|
throw it
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isSuccess) return result.getOrThrow()
|
||||||
|
}
|
||||||
|
error("Unreachable code: retry must throw latest exception if error happen or success value if not")
|
||||||
|
}
|
@@ -15,5 +15,5 @@ crypto_js_version=4.1.1
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.24.6
|
version=0.24.7
|
||||||
android_code_version=286
|
android_code_version=287
|
||||||
|
@@ -10,7 +10,7 @@ jb-compose = "1.7.3"
|
|||||||
jb-exposed = "0.59.0"
|
jb-exposed = "0.59.0"
|
||||||
jb-dokka = "2.0.0"
|
jb-dokka = "2.0.0"
|
||||||
|
|
||||||
sqlite = "3.49.0.0"
|
sqlite = "3.49.1.0"
|
||||||
|
|
||||||
korlibs = "5.4.0"
|
korlibs = "5.4.0"
|
||||||
uuid = "0.8.4"
|
uuid = "0.8.4"
|
||||||
@@ -23,7 +23,7 @@ koin = "4.0.2"
|
|||||||
|
|
||||||
okio = "3.10.2"
|
okio = "3.10.2"
|
||||||
|
|
||||||
ksp = "2.1.10-1.0.29"
|
ksp = "2.1.10-1.0.30"
|
||||||
kotlin-poet = "1.18.1"
|
kotlin-poet = "1.18.1"
|
||||||
|
|
||||||
versions = "0.51.0"
|
versions = "0.51.0"
|
||||||
|
@@ -17,7 +17,7 @@ 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: KeyValueRepo<IdType, ObjectType>,
|
protected open val kvCache: KeyValueRepo<IdType, ObjectType>,
|
||||||
protected val locker: SmartRWLocker = SmartRWLocker(),
|
protected open 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 suspend inline fun <T> doOrTakeAndActualize(
|
protected suspend inline fun <T> doOrTakeAndActualize(
|
||||||
@@ -95,11 +95,11 @@ fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.cached(
|
|||||||
|
|
||||||
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: KeyValueRepo<IdType, ObjectType>,
|
override val kvCache: KeyValueRepo<IdType, ObjectType>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
skipStartInvalidate: Boolean = false,
|
skipStartInvalidate: Boolean = false,
|
||||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
override val locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||||
idGetter: (ObjectType) -> IdType
|
override val idGetter: (ObjectType) -> IdType
|
||||||
) : FullReadCRUDCacheRepo<ObjectType, IdType>(
|
) : FullReadCRUDCacheRepo<ObjectType, IdType>(
|
||||||
parentRepo,
|
parentRepo,
|
||||||
kvCache,
|
kvCache,
|
||||||
|
@@ -17,7 +17,7 @@ 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: KeyValueRepo<Key, Value>,
|
protected open val kvCache: KeyValueRepo<Key, Value>,
|
||||||
protected val locker: SmartRWLocker = SmartRWLocker()
|
protected open val locker: SmartRWLocker = SmartRWLocker()
|
||||||
) : ReadKeyValueRepo<Key, Value>, FullCacheRepo {
|
) : ReadKeyValueRepo<Key, Value>, FullCacheRepo {
|
||||||
protected suspend inline fun <T> doOrTakeAndActualize(
|
protected suspend inline fun <T> doOrTakeAndActualize(
|
||||||
action: KeyValueRepo<Key, Value>.() -> Optional<T>,
|
action: KeyValueRepo<Key, Value>.() -> Optional<T>,
|
||||||
@@ -127,10 +127,10 @@ fun <Key, Value> WriteKeyValueRepo<Key, Value>.caching(
|
|||||||
|
|
||||||
open class FullKeyValueCacheRepo<Key,Value>(
|
open class FullKeyValueCacheRepo<Key,Value>(
|
||||||
override val parentRepo: KeyValueRepo<Key, Value>,
|
override val parentRepo: KeyValueRepo<Key, Value>,
|
||||||
kvCache: KeyValueRepo<Key, Value>,
|
override val kvCache: KeyValueRepo<Key, Value>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
skipStartInvalidate: Boolean = false,
|
skipStartInvalidate: Boolean = false,
|
||||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
override val locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||||
) : //FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
|
) : //FullWriteKeyValueCacheRepo<Key,Value>(parentRepo, kvCache, scope),
|
||||||
KeyValueRepo<Key,Value>,
|
KeyValueRepo<Key,Value>,
|
||||||
WriteKeyValueRepo<Key,Value> by parentRepo,
|
WriteKeyValueRepo<Key,Value> by parentRepo,
|
||||||
|
@@ -18,7 +18,7 @@ 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: KeyValueRepo<Key, List<Value>>,
|
protected open val kvCache: KeyValueRepo<Key, List<Value>>,
|
||||||
protected val locker: SmartRWLocker = SmartRWLocker(),
|
protected open val locker: SmartRWLocker = SmartRWLocker(),
|
||||||
) : ReadKeyValuesRepo<Key, Value>, FullCacheRepo {
|
) : ReadKeyValuesRepo<Key, Value>, FullCacheRepo {
|
||||||
protected suspend inline fun <T> doOrTakeAndActualize(
|
protected suspend inline fun <T> doOrTakeAndActualize(
|
||||||
action: KeyValueRepo<Key, List<Value>>.() -> Optional<T>,
|
action: KeyValueRepo<Key, List<Value>>.() -> Optional<T>,
|
||||||
@@ -201,10 +201,10 @@ fun <Key, Value> WriteKeyValuesRepo<Key, Value>.caching(
|
|||||||
|
|
||||||
open class FullKeyValuesCacheRepo<Key,Value>(
|
open class FullKeyValuesCacheRepo<Key,Value>(
|
||||||
override val parentRepo: KeyValuesRepo<Key, Value>,
|
override val parentRepo: KeyValuesRepo<Key, Value>,
|
||||||
kvCache: KeyValueRepo<Key, List<Value>>,
|
override val kvCache: KeyValueRepo<Key, List<Value>>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
skipStartInvalidate: Boolean = false,
|
skipStartInvalidate: Boolean = false,
|
||||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
override val locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||||
) : KeyValuesRepo<Key, Value>,
|
) : KeyValuesRepo<Key, Value>,
|
||||||
FullReadKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, locker),
|
FullReadKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, locker),
|
||||||
WriteKeyValuesRepo<Key, Value> by parentRepo {
|
WriteKeyValuesRepo<Key, Value> by parentRepo {
|
||||||
|
@@ -17,7 +17,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
open class DirectFullReadCRUDCacheRepo<ObjectType, IdType>(
|
open class DirectFullReadCRUDCacheRepo<ObjectType, IdType>(
|
||||||
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
|
protected open val parentRepo: ReadCRUDRepo<ObjectType, IdType>,
|
||||||
protected open val kvCache: KeyValueRepo<IdType, ObjectType>,
|
protected open val kvCache: KeyValueRepo<IdType, ObjectType>,
|
||||||
protected val locker: SmartRWLocker = SmartRWLocker(),
|
protected open val locker: SmartRWLocker = SmartRWLocker(),
|
||||||
protected open val idGetter: (ObjectType) -> IdType
|
protected open val idGetter: (ObjectType) -> IdType
|
||||||
) : ReadCRUDRepo<ObjectType, IdType>, DirectFullCacheRepo {
|
) : ReadCRUDRepo<ObjectType, IdType>, DirectFullCacheRepo {
|
||||||
protected open suspend fun actualizeAll() {
|
protected open suspend fun actualizeAll() {
|
||||||
@@ -61,10 +61,10 @@ fun <ObjectType, IdType> ReadCRUDRepo<ObjectType, IdType>.directlyCached(
|
|||||||
|
|
||||||
open class DirectFullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
open class DirectFullCRUDCacheRepo<ObjectType, IdType, InputValueType>(
|
||||||
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
|
override val parentRepo: CRUDRepo<ObjectType, IdType, InputValueType>,
|
||||||
kvCache: KeyValueRepo<IdType, ObjectType>,
|
override val kvCache: KeyValueRepo<IdType, ObjectType>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
skipStartInvalidate: Boolean = false,
|
skipStartInvalidate: Boolean = false,
|
||||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
override val locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||||
idGetter: (ObjectType) -> IdType
|
idGetter: (ObjectType) -> IdType
|
||||||
) : DirectFullReadCRUDCacheRepo<ObjectType, IdType>(
|
) : DirectFullReadCRUDCacheRepo<ObjectType, IdType>(
|
||||||
parentRepo,
|
parentRepo,
|
||||||
|
@@ -20,7 +20,7 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
open class DirectFullReadKeyValueCacheRepo<Key, Value>(
|
open class DirectFullReadKeyValueCacheRepo<Key, Value>(
|
||||||
protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
|
protected open val parentRepo: ReadKeyValueRepo<Key, Value>,
|
||||||
protected open val kvCache: KeyValueRepo<Key, Value>,
|
protected open val kvCache: KeyValueRepo<Key, Value>,
|
||||||
protected val locker: SmartRWLocker = SmartRWLocker()
|
protected open val locker: SmartRWLocker = SmartRWLocker()
|
||||||
) : DirectFullCacheRepo, ReadKeyValueRepo<Key, Value> {
|
) : DirectFullCacheRepo, ReadKeyValueRepo<Key, Value> {
|
||||||
protected open suspend fun actualizeAll() {
|
protected open suspend fun actualizeAll() {
|
||||||
kvCache.actualizeAll(parentRepo, locker)
|
kvCache.actualizeAll(parentRepo, locker)
|
||||||
@@ -102,10 +102,10 @@ fun <Key, Value> WriteKeyValueRepo<Key, Value>.directlyCached(
|
|||||||
|
|
||||||
open class DirectFullKeyValueCacheRepo<Key, Value>(
|
open class DirectFullKeyValueCacheRepo<Key, Value>(
|
||||||
override val parentRepo: KeyValueRepo<Key, Value>,
|
override val parentRepo: KeyValueRepo<Key, Value>,
|
||||||
kvCache: KeyValueRepo<Key, Value>,
|
override val kvCache: KeyValueRepo<Key, Value>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
skipStartInvalidate: Boolean = false,
|
skipStartInvalidate: Boolean = false,
|
||||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
override val locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||||
) : DirectFullCacheRepo,
|
) : DirectFullCacheRepo,
|
||||||
KeyValueRepo<Key, Value> ,
|
KeyValueRepo<Key, Value> ,
|
||||||
WriteKeyValueRepo<Key, Value> by DirectFullWriteKeyValueCacheRepo(
|
WriteKeyValueRepo<Key, Value> by DirectFullWriteKeyValueCacheRepo(
|
||||||
|
@@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.*
|
|||||||
open class DirectFullReadKeyValuesCacheRepo<Key,Value>(
|
open class DirectFullReadKeyValuesCacheRepo<Key,Value>(
|
||||||
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
|
protected open val parentRepo: ReadKeyValuesRepo<Key, Value>,
|
||||||
protected open val kvCache: KeyValueRepo<Key, List<Value>>,
|
protected open val kvCache: KeyValueRepo<Key, List<Value>>,
|
||||||
protected val locker: SmartRWLocker = SmartRWLocker(),
|
protected open val locker: SmartRWLocker = SmartRWLocker(),
|
||||||
) : ReadKeyValuesRepo<Key, Value>, DirectFullCacheRepo {
|
) : ReadKeyValuesRepo<Key, Value>, DirectFullCacheRepo {
|
||||||
protected open suspend fun actualizeKey(k: Key) {
|
protected open suspend fun actualizeKey(k: Key) {
|
||||||
kvCache.actualizeAll(locker = locker, clearMode = ActualizeAllClearMode.Never) {
|
kvCache.actualizeAll(locker = locker, clearMode = ActualizeAllClearMode.Never) {
|
||||||
@@ -136,10 +136,10 @@ fun <Key, Value> WriteKeyValuesRepo<Key, Value>.directlyCached(
|
|||||||
|
|
||||||
open class DirectFullKeyValuesCacheRepo<Key,Value>(
|
open class DirectFullKeyValuesCacheRepo<Key,Value>(
|
||||||
override val parentRepo: KeyValuesRepo<Key, Value>,
|
override val parentRepo: KeyValuesRepo<Key, Value>,
|
||||||
kvCache: KeyValueRepo<Key, List<Value>>,
|
override val kvCache: KeyValueRepo<Key, List<Value>>,
|
||||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||||
skipStartInvalidate: Boolean = false,
|
skipStartInvalidate: Boolean = false,
|
||||||
locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
override val locker: SmartRWLocker = SmartRWLocker(writeIsLocked = !skipStartInvalidate),
|
||||||
) : KeyValuesRepo<Key, Value>,
|
) : KeyValuesRepo<Key, Value>,
|
||||||
DirectFullReadKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, locker),
|
DirectFullReadKeyValuesCacheRepo<Key, Value>(parentRepo, kvCache, locker),
|
||||||
WriteKeyValuesRepo<Key, Value> by parentRepo {
|
WriteKeyValuesRepo<Key, Value> by parentRepo {
|
||||||
|
@@ -4,7 +4,6 @@ import dev.inmo.micro_utils.repos.KeyValuesRepo
|
|||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
|
||||||
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
import org.jetbrains.exposed.sql.statements.UpdateBuilder
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
@@ -57,18 +56,48 @@ abstract class AbstractExposedKeyValuesRepo<Key, Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction(database) {
|
val (oldObjects, insertedResults) = transaction(database) {
|
||||||
|
val oldObjects = selectAll().where { selectByIds(toSet.keys.toList()) }.map { it.asKey to it.asObject }
|
||||||
|
|
||||||
deleteWhere {
|
deleteWhere {
|
||||||
selectByIds(it, toSet.keys.toList())
|
selectByIds(it, toSet.keys.toList())
|
||||||
}
|
}
|
||||||
batchInsert(
|
val inserted = batchInsert(
|
||||||
prepreparedData,
|
prepreparedData,
|
||||||
) { (k, v) ->
|
) { (k, v) ->
|
||||||
insert(k, v, this)
|
insert(k, v, this)
|
||||||
}.map {
|
}.map {
|
||||||
it.asKey to it.asObject
|
it.asKey to it.asObject
|
||||||
}
|
}
|
||||||
}.forEach { _onNewValue.emit(it) }
|
oldObjects to inserted
|
||||||
|
}.let {
|
||||||
|
val mappedFirst = it
|
||||||
|
.first
|
||||||
|
.asSequence()
|
||||||
|
.groupBy { it.first }
|
||||||
|
.mapValues { it.value.map { it.second }.toSet() }
|
||||||
|
val mappedSecond = it
|
||||||
|
.second
|
||||||
|
.asSequence()
|
||||||
|
.groupBy { it.first }
|
||||||
|
.mapValues { it.value.map { it.second }.toSet() }
|
||||||
|
mappedFirst to mappedSecond
|
||||||
|
}
|
||||||
|
val deletedResults = oldObjects.mapNotNull { (k, vs) ->
|
||||||
|
k to vs.filter { v ->
|
||||||
|
insertedResults[k] ?.contains(v) != true
|
||||||
|
}.ifEmpty { return@mapNotNull null }
|
||||||
|
}
|
||||||
|
deletedResults.forEach { (k, vs) ->
|
||||||
|
vs.forEach { v ->
|
||||||
|
_onValueRemoved.emit(k to v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertedResults.forEach { (k, vs) ->
|
||||||
|
vs.forEach { v ->
|
||||||
|
_onNewValue.emit(k to v)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||||
|
Reference in New Issue
Block a user