Search the site
DATA
DV.Cache is four calls, get, set, has and delete, from a page or a backend function.
Everything else is an option on those four: get computes a missing value, set takes tags, and delete drops a key, a tag or everything.
ON THIS PAGE
Get, set, has and delete
Compute a value on a miss
Drop a group of keys by tag
Serve stale data while it refreshes
Choose where the cache lives
Run work once
Manage a database cache from the CLI
Public HTML is cached automatically
Status
await DV.Cache.set('greeting', 'hello', ttl: const Duration(hours: 1));
final String? greeting = await DV.Cache.get<String>('greeting');
final bool cached = await DV.Cache.has('greeting');
await DV.Cache.delete('greeting');Copy code to clipboard
ttl is named. Without one an entry stays until it is deleted.
get returns null for a missing key, an expired one, or a value of another type.
A backend function uses the same calls, with DV from package:dartvel_core/dv.dart.
Future<List<String>?> productNames() => DV.Cache.get<List<String>>(
'products:names',
compute: fetchProductNames, // runs only when the key is missing or expired
ttl: const Duration(minutes: 10),
tags: <String>['products'],
);Copy code to clipboard
The compute runs only on a miss. Callers that ask for the same key at once share one compute.
A compute that throws stores nothing.
ttl, tags and staleFor apply to what the compute stores. Passing one to get without a compute is an error.
await DV.Cache.set('home:featured', <String>['Starter kit'],
tags: <String>['products', 'home']);
await DV.Cache.delete(const DVCacheTag('products')); // every key tagged products
await DV.Cache.delete(DVCache.all); // every keyCopy code to clipboard
set and get take tags. delete(DVCacheTag(...)) removes every key with that tag, and delete(DVCache.all) removes every key.
delete takes one argument: a String key, a DVCacheTag or DVCache.all. Anything else is an error, never a guess.
Tags are kept in each server's memory. Deleting a tag drops the keys this server has tagged.
final List<String>? names = await DV.Cache.get<List<String>>(
'products:names',
compute: fetchProductNames,
ttl: const Duration(minutes: 1),
staleFor: const Duration(minutes: 10),
);Copy code to clipboard
After ttl and for staleFor more, callers get the old value at once while one compute refreshes it. After both, they wait for a new value. staleFor needs a ttl.
# pubspec.yaml
dartvel:
cache:
store: redis # memory (default), database, redis or memcached
url: ${REDIS_URL} # read from the environment when the server starts
prefix: "shop:"Copy code to clipboard
memory
Keeps entries in: The server process. The default
database
Keeps entries in: The database in DATABASE_URL, table dartvel_cache. Change it with table:
redis
Keeps entries in: Redis or Valkey, at url
memcached
Keeps entries in: Memcached, at url
url reads an environment variable, such as ${REDIS_URL}. The build refuses a password written into pubspec.yaml.
prefix starts every key, so two applications can share one server. The default is dartvel:.
The server refuses to start when it cannot reach the store.
On a device, memory is the default store. The store in pubspec.yaml applies only to the server.
A device can switch store in code with withAdapter, below. Sharing a cache with the server is planned.
Keys are prefixed with the tenant unless it is the default one.
pubspec.yaml sets the store DV.Cache uses by default. DV.Cache.withAdapter switches to another store in code, with the same four calls:
final DVCacheView sessions = DV.Cache.withAdapter(
await DVRedisCacheAdapter.connect(DV.Secrets.get('SESSIONS_REDIS_URL')),
);
await sessions.set('visitor:42', 'signed in', ttl: const Duration(hours: 8));
final bool active = await sessions.has('visitor:42');Copy code to clipboard
Every call goes to the adapter you pass, never to the default store.
The adapters are DVMemoryCacheAdapter, DVDatabaseCacheAdapter, DVRedisCacheAdapter, DVMemcachedCacheAdapter and DVDistributedCacheAdapter.
Tags and the shared compute are kept per adapter, so deleting a tag on one store leaves the others alone.
For work only one server should do, such as a monthly report, declare a schedule. Each occurrence is claimed once across cron processes, and a process does not start a schedule again while its last run is still going. Unique jobs are not built yet.
dartvel cache purge --database dartvel.db # drop expired entries
dartvel cache clear --database dartvel.dbCopy code to clipboard
dartvel build web-serverCopy code to clipboard
Since 0.11.5, safe public GET pages with compiled content reuse their rendered HTML after the first request. This is automatic and separate from the four DV.Cache calls.
Cookies, authorization, guarded routes and runtime page data bypass the document cache. Studio saves and publishes and successful OTA updates purge it. ETags allow revalidation with a 304 response. Use cache: false on a DVRoute in route configuration to opt out.
Planned
Invalidation across server processes is not yet implemented. Each process has its own bounded cache.
Partial
Spec section: Cache
Planned work and implementation limits
No model query cache, and no caching a backend function by annotation.
Model writes do not drop tags yet. Call delete(DVCacheTag(...)) yourself.
A device cannot share the server's cache yet. The plan is an adapter that calls the server, with every call checked against a policy and scoped to the tenant.
Several Redis or Memcached nodes are not a store pubspec.yaml can name yet. Pass a DVDistributedCacheAdapter to withAdapter instead.
FSL-1.1-MIT licensed. Built with Dartvel.
Dartvel is made by
To the bottom