reactivecircus / cache4k Goto Github PK
View Code? Open in Web Editor NEWIn-memory Cache for Kotlin Multiplatform.
Home Page: https://reactivecircus.github.io/cache4k/
License: Apache License 2.0
In-memory Cache for Kotlin Multiplatform.
Home Page: https://reactivecircus.github.io/cache4k/
License: Apache License 2.0
Following #39 we want to fix the remaining linearizability surfaced by Lincheck.
java.util.ConcurrentModificationException
java.base/java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:756)
java.base/java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:778)
io.github.reactivecircus.cache4k.RealCache.expireEntries(RealCache.kt:174)
io.github.reactivecircus.cache4k.RealCache.put(RealCache.kt:120)
Hey , Thanks for your work, I am interested in using your Lib in one of my projects : SpotiFlyer,
But unfortunately, It crashes as Runtime.
java.lang.NoSuchMethodError: No virtual method getINFINITE-UwyO8pc()D in class Lkotlin/time/Duration$Companion; or its super classes (declaration of 'kotlin.time.Duration$Companion' appears in /data/data/com.shabinder.spotiflyer/code_cache/.overlay/base.apk/classes3.dex)
at io.github.reactivecircus.cache4k.CacheBuilderImpl.<init>(Cache.kt:109)
at io.github.reactivecircus.cache4k.Cache$Builder$Companion.newBuilder(Cache.kt:99)
In the current implementation null is returned when an element is not in the cache. There are use cases where the actual value itself is null. Support for null values would be good.
The overloaded get method accepts a suspendable loader function. Writing get(key, () -> Value) in every place makes the code too verbose when the loading logic is same for all calls. So, having a cache which accepts loader in the constructor to create value in the cache if it doesn't already have would be nice.
We have implemented a wrapper with a getValue
method which returns the value or null to solve these problems. Having such an API as part of the library would be good.
Kotlin has just released 1.7.0-Beta.
But this version raise an exception:
java.lang.IncompatibleClassChangeError: Found interface kotlin.time.TimeMark, but class was expected
at io.github.reactivecircus.cache4k.RealCache.recordWrite(RealCache.kt:236)
at io.github.reactivecircus.cache4k.RealCache.put(RealCache.kt:136)
at io.github.reactivecircus.cache4k.RealCache$get$3.invokeSuspend(RealCache.kt:112)
It's appear when we use expire
.
Cache.Builder()
.maximumCacheSize(100)
.expireAfterWrite(5.minutes)
.build<String, String>()
Kotlin 1.6.20
public object Monotonic : TimeSource by MonotonicTimeSource {
override fun toString(): String = MonotonicTimeSource.toString()
}
Kotlin 1.7.0-Beta
public object Monotonic : TimeSource {
override fun markNow(): ValueTimeMark = MonotonicTimeSource.markNow()
override fun toString(): String = MonotonicTimeSource.toString()
@ExperimentalTime
@SinceKotlin("1.7")
@JvmInline
public value class ValueTimeMark internal constructor(internal val reading: ValueTimeMarkReading) : TimeMark {
override fun elapsedNow(): Duration = MonotonicTimeSource.elapsedFrom(this)
override fun plus(duration: Duration): ValueTimeMark = MonotonicTimeSource.adjustReading(this, duration)
override fun minus(duration: Duration): ValueTimeMark = MonotonicTimeSource.adjustReading(this, -duration)
override fun hasPassedNow(): Boolean = !elapsedNow().isNegative()
override fun hasNotPassedNow(): Boolean = elapsedNow().isNegative()
}
}
same key is expiring twice with this config
val cache = Cache.Builder<String, String>()
.maximumCacheSize(1_00)
.expireAfterWrite(2.seconds)
.expireAfterAccess(5.seconds)
.eventListener { event ->
when (event) {
is CacheEvent.Created -> {}
is CacheEvent.Updated -> {}
is CacheEvent.Evicted -> {
scope.launch {
println("Evicted ${event.key}")
}
}
is CacheEvent.Expired -> {
scope.launch {
println("Expire ${event.key}")
}
}
is CacheEvent.Removed -> {
println("Evicted")
}
}
}
.build()
here I launch different coroutine adding value in the cache
for (n in 0..100) {
delay(100)
scopeL.launch {
schemas.put("n$n", "some value")
println("n$n")
}
}
Result
...
adding n18
adding n19
adding n20
Expire n0
Expire n2
Expire n1
Expire n0
Expire n1
Expire n2
adding n21
Expire n3
Expire n3
adding n22
Expire n4
Expire n4
adding n23
Expire n5
Expire n5
adding n24
Expire n6
Expire n6
adding n25
...
The same keys are expiring twice
In my application, I'd like to cache instances of an object which require to be shut down properly via a shutdown()
method.
I want to cache them, since idk how expensive they are to instantiate and sometimes they might be used frequently for a bit, before not being needed for a while.
The only problem is, I can't add an eviction listener to the Cache.
Something along the lines of
Cache.Builder.newBuilder()
.expireAfterAccess(hours(4))
.maximumCacheSize(50)
.expiryListener { it -> it.shutdown }
.build()
would be nice.
Please, add support for linuxX64 platform. I have checked all tests are passing properly. Just add it in kotlin.gradle.kts.
In one of my projects Dependabot wants to update cache4k from 0.9.0
to 0.10.0
but now the build fails with
Execution failed for task ':app:checkDebugDuplicateClasses'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
> Duplicate class co.touchlab.stately.HelpersJVMKt found in modules stately-common-jvm-2.0.0-rc1 (co.touchlab:stately-common-jvm:2.0.0-rc1) and stately-strict-jvm-2.0.0-rc1 (co.touchlab:stately-strict-jvm:2.0.0-rc1)
Note: This is an Android/JVM project, no multiplatform project.
Hi,
put and get caching is working for the first time but when the user exits app and tries to get cache data it prints null.
Am using koltin
`
val cache = Cache.Builder()
.expireAfterAccess(1.minutes).build<Long, String>()
saveValue.setOnClickListener { cache.put(1,"kona") }
getValue.setOnClickListener { Log.d("value",""+cache?.get(1)) }`
Any info more needed I will provide
Updating from 0.8.0 to 0.9.0 is introducing massive performance issues in benchmarks that I'm running. Ranges from 100-1000X slower with 0.9.0
I would like to unit test the cache expiration time.
We would be happy if users could also reuse the FakeTimeSource mentioned in the documentation!
Do you plan to release a set of classes related to testing, including FakeTimeSource?
https://github.com/ReactiveCircus/cache4k#unit-testing-cache-expirations
Using this library for JVM I noticed that my app based on compose-jb
won't exit properly.
I suspect that there is a thread running in the background that prevents shutdown, but I don't see a option to shutdown the cache.
I suggest that for time-based evictions you should use a daemon thread.
Please add support for the various tvos targets as well
tvosArm64
tvosSimulatorArm64
tvosX64
code
fun storeCredentials(value: String) {
val cache = Cache.Builder<String, String>().build()
cache.put(KeyConstant.userCredentials, value)
val value = cache.get(KeyConstant.userCredentials)
print(value)
}
fun retrieveCredentials(): String? {
val cache = Cache.Builder<String, String>().build()
val creds = cache.get(KeyConstant.userCredentials)
return creds
}
If you run this code
runBlocking {
launch { cache.get(0) { fetchUser() } }
launch { cache.get(0) { fetchUser() } }
launch { cache.get(0) { fetchUser() } }
launch { cache.get(0) { fetchUser() } }
}
suspend fun fetchUser(): Int {
delay(100)
println("Hitting the network to fetch user!")
return 0
}
then the message "Hitting the network to fetch user!" is printed 4 times.
I would expect this cache to be synchronised by key in a way that when multiple threads are trying to read a value from a key in which the value is suspend then it will await till the value is computed and then deliver it, rather than calling it once per thread and then race to see who writes to the cache first.
@ychescale9 I see that you enabled the new memory model in a previous commit. Do you plan to only support the new memory model in the future or do you wish to support both?
In the new memory model there would no longer be a need for IsoMap. Just safe concurrent access.
I'd be happy to work on an implementation if you let me know which route you were intending to take.
Is it possible to set the expiration time during execution of the loader?
Trying to cache some oauth tokens whose expiration time is known when the loader gets them. Not before.
This causes it to be typed as Any
when trying to consume it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.