All Projects → ReactiveCircus → cache4k

ReactiveCircus / cache4k

Licence: Apache-2.0 license
In-memory Cache for Kotlin Multiplatform.

Programming Languages

kotlin
9241 projects
shell
77523 projects

Projects that are alternatives of or similar to cache4k

DagashiApp
Unofficial Android Dagashi Android/iOS app.
Stars: ✭ 32 (-74.19%)
Mutual labels:  kotlin-multiplatform
trikot.foundation
Core utilities for Kotlin Multiplatform
Stars: ✭ 21 (-83.06%)
Mutual labels:  kotlin-multiplatform
moko-web3
Ethereum Web3 implementation for mobile (android & ios) Kotlin Multiplatform development
Stars: ✭ 32 (-74.19%)
Mutual labels:  kotlin-multiplatform
kfsm
Finite State Machine in Kotlin
Stars: ✭ 76 (-38.71%)
Mutual labels:  kotlin-multiplatform
libs.kmp.icerock.dev
Kotlin Multiplatform libraries list with info auto-fetch
Stars: ✭ 178 (+43.55%)
Mutual labels:  kotlin-multiplatform
tv-maniac
Tv-Maniac is a Multiplatform app (Android & iOS) for viewing TV Shows from TMDB.
Stars: ✭ 55 (-55.65%)
Mutual labels:  kotlin-multiplatform
KMP-NativeCoroutines
Library to use Kotlin Coroutines from Swift code in KMP apps
Stars: ✭ 502 (+304.84%)
Mutual labels:  kotlin-multiplatform
kmpapp
👨‍💻 Kotlin Mobile Multiplatform App (Android & iOS). One Code To Rule Them All. MVVM, DI (Kodein), coroutines, livedata, ktor, serialization, mockk, detekt, ktlint, jacoco
Stars: ✭ 34 (-72.58%)
Mutual labels:  kotlin-multiplatform
island-time
A Kotlin Multiplatform library for working with dates and times
Stars: ✭ 69 (-44.35%)
Mutual labels:  kotlin-multiplatform
kgl
Lightweight OpenGL abstraction for Kotlin Multiplatform
Stars: ✭ 21 (-83.06%)
Mutual labels:  kotlin-multiplatform
kmp-fatframework-cocoa
A Gradle plugin to generate and publish an iOs FatFramework or XCFramework on Kotlin Multiplatform projects.
Stars: ✭ 26 (-79.03%)
Mutual labels:  kotlin-multiplatform
devtools-library
Multiplatform, pluggable, extensible, and dynamic config library which supports YAML and JSON as a source.
Stars: ✭ 15 (-87.9%)
Mutual labels:  kotlin-multiplatform
Sheasy
This an Android App that helps you share/manage your files on your Android Device through a WebInterface in the Browser - Built with Ktor and Kotlin-React
Stars: ✭ 34 (-72.58%)
Mutual labels:  kotlin-multiplatform
realm-kotlin-samples
Samples demonstrating the usage of Realm-Kotlin SDK
Stars: ✭ 50 (-59.68%)
Mutual labels:  kotlin-multiplatform
kgql
GraphQL Document wrapper generator for Kotlin Multiplatform Project and Android
Stars: ✭ 54 (-56.45%)
Mutual labels:  kotlin-multiplatform
PreCompose
Compose Multiplatform Navigation && ViewModel
Stars: ✭ 150 (+20.97%)
Mutual labels:  kotlin-multiplatform
moko-network
Network components with codegeneration of rest api for mobile (android & ios) Kotlin Multiplatform development
Stars: ✭ 107 (-13.71%)
Mutual labels:  kotlin-multiplatform
sweekt
🍭 Some sugar to sweeten Kotlin development.
Stars: ✭ 35 (-71.77%)
Mutual labels:  kotlin-multiplatform
moko-maps
Control your map from common code for mobile (android & ios) Kotlin Multiplatform development
Stars: ✭ 47 (-62.1%)
Mutual labels:  kotlin-multiplatform
Notflix
Kotlin Multiplatform playground
Stars: ✭ 272 (+119.35%)
Mutual labels:  kotlin-multiplatform

cache4k

CI Maven Central License

In-memory Cache for Kotlin Multiplatform.

Work in progress.

cache4k provides a simple in-memory key-value cache for Kotlin Multiplatform, with support for time-based (expiration) and size-based evictions.

Note that only the new Kotlin Native memory model is supported.

The following targets are currently supported:

  • jvm
  • js
  • iosX64
  • iosArm64
  • iosSimulatorArm64
  • macosX64
  • macosArm64
  • tvosArm64
  • tvosSimulatorArm64
  • tvosX64

Download

Dependencies are hosted on Maven Central.

Android

dependencies {
    implementation("io.github.reactivecircus.cache4k:cache4k:x.y.z")
}

Multiplatform

kotlin {
    sourceSets {
        commonMain {
            dependencies {
                implementation("io.github.reactivecircus.cache4k:cache4k:x.y.z")
            }
        }
    }
}

Usage

Writing and reading cache entries

To create a new Cache instance using Long for the key and String for the value:

val cache = Cache.Builder().build<Long, String>()

To start writing entries to the cache:

cache.put(1, "dog")
cache.put(2, "cat")

To read a cache entry by key:

cache.get(1) // returns "dog"
cache.get(2) // returns "cat"
cache.get(3) // returns null

To overwrite an existing cache entry:

cache.put(1, "dog")
cache.put(1, "bird")
cache.get(1) // returns "bird"

Cache loader

Cache provides an API for getting cached value by key and using the provided loader: suspend () -> Value lambda to compute and cache the value automatically if none exists.

runBlockingTest {
    val cache = Cache.Builder().build<Long, User>()

    val userId = 1L
    val user = cache.get(userId) {
        fetchUserById(userId) // potentially expensive call (might be a suspend function)
    }

    // value successfully computed by the loader will be cached automatically
    assertThat(user).isEqualTo(cache.get(userId))
}

Note that loader is executed on the caller's coroutine context. Concurrent calls from multiple threads using the same key will be blocked. Assuming the 1st call successfully computes a new value, none of the loader from the other calls will be executed and the cached value computed by the first loader will be returned for those calls.

Any exceptions thrown by the loader will be propagated to the caller of this function.

Expirations and evictions

By default, Cache has an unlimited number of entries which never expire. But a cache can be configured to support both time-based expirations and size-based evictions.

Time-based expiration

Expiration time can be specified for entries in the cache.

Expire after access

To set the maximum time an entry can live in the cache since the last access (also known as ** time-to-idle**), where "access" means reading the cache, adding a new cache entry, or ** replacing an existing entry with a new one**:

val cache = Cache.Builder()
    .expireAfterAccess(24.hours)
    .build<Long, String>()

An entry in this cache will be removed if it has not been read or replaced after 24 hours since it's been written into the cache.

Expire after write

To set the maximum time an entry can live in the cache since the last write (also known as ** time-to-live**), where "write" means adding a new cache entry or replacing an existing entry with a new one:

val cache = Cache.Builder()
    .expireAfterWrite(30.minutes)
    .build<Long, String>()

An entry in this cache will be removed if it has not been replaced after 30 minutes since it's been written into the cache.

Note that cache entries are not removed immediately upon expiration at exact time. Expirations are checked in each interaction with the cache.

Size-based eviction

To set the maximum number of entries to be kept in the cache:

val cache = Cache.Builder()
    .maximumCacheSize(100)
    .build<Long, String>()

Once there are more than 100 entries in this cache, the least recently used one will be removed, where "used" means reading the cache, adding a new cache entry, or replacing an existing entry with a new one.

Getting all cache entries as a Map

To get a copy of the current cache entries as a Map:

val cache = Cache.Builder()
    .build<Long, String>()

cache.put(1, "dog")
cache.put(2, "cat")

assertThat(cache.asMap())
    .isEqualTo(mapOf(1L to "dog", 2L to "cat"))

Note that calling asMap() has no effect on the access expiry of the cache.

Deleting cache entries

Cache entries can also be deleted explicitly.

To delete a cache entry for a given key:

val cache = Cache.Builder().build<Long, String>()
cache.put(1, "dog")

cache.invalidate(1)

assertThat(cache.get(1)).isNull()

To delete all entries in the cache:

cache.invalidateAll()

Unit testing cache expirations

To test logic that depends on cache expiration, pass in a fake TimeSource implementation when building a Cache so you can programmatically advance the reading of the time source:

@Test
fun cacheEntryEvictedAfterExpiration() {
    private val fakeTimeSource = FakeTimeSource()
    val cache = Cache.Builder()
        .timeSource(fakeTimeSource)
        .expireAfterWrite(1.minutes)
        .build<Long, String>()

    cache.put(1, "dog")

    // just before expiry
    fakeTimeSource += 1.minutes - 1.nanoseconds

    assertThat(cache.get(1))
        .isEqualTo("dog")

    // now expires
    fakeTimeSource += 1.nanoseconds

    assertThat(cache.get(1))
        .isNull()
}

A sample FakeTimeSource implementation can be found in our tests.

Credits

The library was ported from a kotlin / JVM cache which I contributed to dropbox/Store to help unblock Store's multiplatform support ( it was reverted before the 1.0 release as multiplatform wasn't a priority). Many thanks to Store's owners and contributors for reviewing and improving the original implementation.

License

Copyright 2021 Yang Chen

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].