The Slate Cache is a light-weight LRU ( Least Recently Used ) Cache for both Android and Server. While there are many comprehensive Cache solutions available for the JVM, this is designed to be an in-memory light-weight cache with an emphasis on diagnostics and async functionality leveraging Kotlin Coroutines and Channels.
A high-level diagram of the concepts in this component
Goal | Description |
1. Light-Weight | Simple, light-weight with default implementations for sync and async based Caches. |
2. Diagnostics | Provides a reasonable level of diagnostics and cache metrics |
3. Coroutines | Async based cache leverages Coroutines / Channels for write operations |
This component is currently stable. Future versions will include support for:
Feature | Status | Description |
Events | Upcoming | An event emitter for changes to cache |
Refresh | Upcoming | Automatic / scheduled refresh of cache items |
Stats | Upcoming | Enhanced stats to capture evictions/clearing of cache |
repositories {
// other repositories
maven { url "http://dl.bintray.com/codehelixinc/slatekit" }
}
dependencies {
// other dependencies ...
compile 'com.slatekit:slatekit-cache:1.0.0'
}
Jar | slatekit.cache.jar |
Package | slatekit.cache |
Sources | slatekit-cache |
Example | Example_Cache.kt |
Version | |
License | |
Requires | See build.gradle for more info. |
import slatekit.cache.*
// SimpleCache is the underlying Cache implementation
val raw:Cache = SimpleCache(CacheSettings(10))
// Synchronized cache wraps the raw cache
// NOTE: Async cache via coroutines/channels avialable (see docs below)
val cache:SyncCache = SimpleSyncCache(raw)
// Writes
// 1. Put new entry ( using a function to fetch )
cache.put("promos", "promotion codes", 300) { listOf("p1", "p2") }
// 2. Force a refresh
cache.refresh("promos")
// Reads
// 1. Get existing cache item
val c1 = cache.get<List<Country>>("countries")
// 2. Get existing cache item or load it if expired
val c2 = cache.getOrLoad<List<String>>("promos")
// Stats
cache.stats().forEach {
println("key : " + it.key)
println("expiry.start : " + it.expiry.started.toStringUtc())
println("expiry.seconds : " + it.expiry.seconds )
println("expiry.expires : " + it.expiry.expires.toStringUtc())
println("hits.count : " + it.hits.count)
println("hits.time : " + it.hits.timestamp?.toStringUtc() )
println("value.created : " + it.value.created?.toStringUtc() )
println("value.updated : " + it.value.updated?.toStringUtc() )
println("value.applied : " + it.value.applied)
println("error.created : " + it.error.created?.toStringUtc() )
println("error.updated : " + it.error.updated?.toStringUtc() )
println("error.applied : " + it.error.applied)
println("\n")
}
Name | Description | More |
1. Usage | Usage of the features | more |
2. Stats | Gettign stats and cache diagnostics | more |
3. Sync | How to convert raw text into parsed parameters | more |
4. Async | Working with parsed commands as CLI Requests | more |
5. Load | Performance and load metrics | more |
Showing simple usage of a synchronous cache implementation all operations on cache are using the @synchronized annotation on methods.
import slatekit.cache.*
// SimpleCache is the underlying Cache implementation
val raw:Cache = SimpleCache(CacheSettings(10))
// Synchronized cache wraps the raw cache
// NOTE: Async cache via coroutines/channels avialable (see docs below)
val cache:SyncCache = SimpleSyncCache(raw)
// Writes: Both sync and async versions have these put/set/refresh methods
// 1. Put new entry ( using a function to fetch )
cache.put("promos", "promotion codes", 300) { listOf("p1", "p2") }
// 2. Update existing entry with value
cache.set("promos", listOf("p1", "p2"))
// 3. Force a refresh
cache.refresh("promos")
// Reads:
// NOTES:
// 1. The sync version returns the value
// 2. The async version returns a Deferred<T>
val c1 = cache.get<List<Country>>("countries")
// 2. Get existing cache item or load it if expired
val c2 = cache.getOrLoad<List<String>>("promos")
// 3. Get after refreshing it first
val c3 = cache.getFresh<List<String>>("promos")
println(c1)
println(c2)
println(c3)
The main difference in the stats for this component is that both the created, updated timestamps are maintained while also typically keeping track of the counts of accesses, hits, misses.
Term | Desc | Example | Ratio |
Accesses | Number of times a cache item is accessed | 100 | n/a |
Hits | Number of times a cache item is accessed and it exists | 80 | .8 |
Misses | Number of times a cache item is accessed but does not exist | 20 | .2 |
// ... setup ( see above )
cache.stats().forEach {
println("key : " + it.key)
println("expiry.start : " + it.expiry.started.toStringUtc())
println("expiry.seconds : " + it.expiry.seconds )
println("expiry.expires : " + it.expiry.expires.toStringUtc())
println("access.count : " + it.reads?.count)
println("access.time : " + it.reads?.timestamp?.toStringUtc() )
println("hits.count : " + it.hits.count)
println("hits.time : " + it.hits.timestamp?.toStringUtc() )
println("misses.count : " + it.misses?.count)
println("misses.time : " + it.misses?.timestamp?.toStringUtc() )
println("value.created : " + it.value.created?.toStringUtc() )
println("value.updated : " + it.value.updated?.toStringUtc() )
println("value.applied : " + it.value.applied)
println("error.created : " + it.error.created?.toStringUtc() )
println("error.updated : " + it.error.updated?.toStringUtc() )
println("error.applied : " + it.error.applied)
println("\n")
}
Showing usage of a synchronous cache implementation SimpleAsyncCache. All operations on cache are using the @synchronized annotation on methods. Synchronouse caches are implemented using the SyncCache interface.
import slatekit.cache.*
// SimpleCache is the underlying Cache implementation
val raw:Cache = SimpleCache(CacheSettings(10))
// Synchronized cache wraps the raw cache
// NOTE: Async cache via coroutines/channels avialable (see docs below)
val cache:SyncCache = SimpleSyncCache(raw)
// Writes
// 1. Put new entry ( using a function to fetch )
cache.put("promos", "promotion codes", 300) { listOf("p1", "p2") }
// 2. Update existing entry with value
cache.set("promos", listOf("p1", "p2"))
// 3. Force a refresh
cache.refresh("promos")
// Reads
// 1. Get existing cache item
val c1 = cache.get<List<Country>>("countries")
// 2. Get existing cache item or load it if expired
val c2 = cache.getOrLoad<List<String>>("promos")
// 3. Get after refreshing it first
val c3 = cache.getFresh<List<String>>("promos")
println(c1)
println(c2)
println(c3)
Async functionality is based on using Coroutines and Channels for writes. The async cache implementation is SimpleAsyncCache. Async caches are implemented using the AsyncCache interface. In this approach, concurrent writes are handling by creating a CacheCommand representing the operation and sending them to a channel to be handled sequentially. Reads are handling by returning a kotlin Deferred[T].
import slatekit.cache.*
val logger = LoggerConsole()
val coordinator = ChannelCoordinator(logger, Paired(), Channel<CacheCommand>(Channel.UNLIMITED))
val asyncCache: AsyncCache = SimpleAsyncCache(raw, coordinator)
// Writes
// 1. Put new entry ( using a function to fetch )
asyncCache.put("promos", "promotion codes", 300) { listOf("p1", "p2") }
// 2. Update existing entry with value
asyncCache.set("promos", listOf("p1", "p2"))
// 3. Force a refresh
asyncCache.refresh("promos")
// Reads
// 1. Get existing cache item
val a1 = asyncCache.get<List<Country>>("countries").await()
// 2. Get existing cache item or load it if expired
val a2 = asyncCache.getOrLoad<List<String>>("promos").await()
// 3. Get after refreshing it first
val a3 = asyncCache.getFresh<List<String>>("promos").await()
Performance and load docs coming soon
Back to features Back to top