Cache (dox_cache)
dox_cache is a cache like Laravel Cache. It has remember, tags, atomic locks and four drivers.
Built into Dox
dox_core uses dox_cache for Cache().
In a Dox app, set the stores in config/cache.dart (the CacheConfig of this page), and use Cache().
Dox().cache is the CacheManager. Cache().repository is the CacheStore.
This page is for the full API and for using dox_cache outside Dox.
Install
Note
dox_core exports CacheConfig, CacheManager, CacheStore and CacheLock from this package.
They are the same classes, so importing both packages is fine.
Drivers
| Driver | What it is | Shared between isolates |
|---|---|---|
memory |
a map in this isolate. Fast. Optional LRU size limit | No |
file |
one JSON file per key in a folder | Yes (same machine) |
redis |
Redis, with the ioredis package | Yes |
null |
stores nothing. Use it to turn caching off | - |
Warning
Dox runs many isolates. Each isolate has its own memory cache.
Use redis when isolates or servers must see the same data.
Set up
CacheManager cache = CacheManager(CacheConfig(
defaultStore: 'redis',
stores: <String, Map<String, dynamic>>{
'memory': <String, dynamic>{'driver': 'memory', 'max_size': 1000},
'file': <String, dynamic>{'driver': 'file', 'path': 'storage/cache'},
'redis': <String, dynamic>{'driver': 'redis', 'host': '127.0.0.1', 'prefix': 'app:'},
},
));
CacheStore store = cache.store(); // the default store
await cache.store('file').get('a'); // a named store
Redis options: host, port, username, password, db, tls, prefix.
Basic use
await store.put('a', 1, Duration(minutes: 5));
await store.get('a'); // 1
await store.get('missing', 'none'); // 'none'
await store.has('a'); // true
await store.forget('a');
No ttl means forever
put without a ttl keeps the value forever.
(The old Cache() in dox_core keeps it for 1 hour.)
A zero or negative ttl removes the key.
Remember
Get the cached value, or run the function and cache the result.
List<dynamic> posts = await store.remember('posts', Duration(minutes: 10), () async {
return await loadPostsAsJson();
});
All methods
| Method | What it does |
|---|---|
get(key, [default]) |
the value, or the default when missing or expired |
put(key, value, [ttl]) |
store it |
add(key, value, [ttl]) |
store only when missing. Returns true when stored |
has(key) |
true when the value is not null |
forget(key) |
remove it. Returns true when it was there |
flush() |
remove all items of this store |
many(keys) / putMany(map, [ttl]) |
many keys at once |
increment(key, [by]) / decrement(key, [by]) |
change a number. A missing key starts at 0. The ttl stays |
forever(key, value) |
store without expiry |
remember(key, ttl, compute) |
see above |
rememberForever(key, compute) |
the same, without expiry |
pull(key) |
get it, then remove it |
tags([...]) |
a tagged view |
lock(name, [ttl]) |
an atomic lock |
Values are JSON
Values must be JSON types: Map, List, String, num, bool, null. Other types throw.
- A
Mapcomes back asMap<String, dynamic>. AListcomes back asList<dynamic>. - A stored
nullreads as a miss.hasreturns false for it. - For a model, store
model.toJson(), not the model.
Tags
await store.tags(<String>['users']).put('u1', 'Ann');
await store.tags(<String>['users']).flush(); // removes only the 'users' items
Tags work with every driver. Old items are not deleted right away. They are just not found any more, and they go away when their ttl ends.
Locks
Only one worker runs the code at a time.
CacheLock lock = store.lock('report', Duration(seconds: 10));
if (await lock.get()) {
try {
// only one worker runs this
} finally {
await lock.release();
}
}
bool got = await lock.block(Duration(seconds: 5)); // wait up to 5 seconds
memory and redis support locks. The memory lock works inside one isolate only.
Prefix
Every store has a prefix (default dox_cache:). It is added to every key.
Use different prefixes when two apps share one Redis database.
Redis flush
flush deletes only the keys with the prefix. It never runs FLUSHDB.
An empty prefix makes flush throw.
Your own driver
class MyDriverFactory implements CacheDriverFactory {
@override
CacheDriver create(Map<String, dynamic> options) {
return MyDriver(options);
}
}
cache.extend('mine', MyDriverFactory()); // config: {'driver': 'mine'}
cache.addStore('ready', MemoryDriver()); // or add a ready driver
MyDriver extends CacheDriver and implements get, put, add, increment, forget and flush.