Engines

Cache

Reads are the common case. Keep them off the database without giving up durability — L1 is memory, L2 is SQLite, and neither needs a Redis instance.

Setup

yatta/func/cache.tsts
import { createCache, SQLiteL2CacheStore } from "yatta/cache"; export const cache = createCache({  maxItems: 20_000,       // L1 capacity  defaultTtl: "1h",  l2Storage: new SQLiteL2CacheStore("Database/cache.db"),});

Drop l2Storage for a purely in-memory cache — useful in tests, or when a restart should reset everything.

Reading and writing

Basicts
await cache.set("user:42", { name: "Ada" }, { ttl: "5m" }); const user = await cache.get<{ name: string }>("user:42");// → { name: "Ada" } | null await cache.delete("user:42");

remember

remember is the useful one. It caches a factory's result, and while a value is being computed only one call runs — the rest wait for it. That removes the thundering herd when a popular key expires.

Singleflight + SWRts
const user = await cache.remember(  `user:${id}`,  "5m",                        // fresh for 5 minutes  () => fetchUserFromDatabase(id),  {    swr: "30m",                // serve stale while refreshing    tags: ["users", `user:${id}`],  },);
Note
With swr set, an expired entry is still returned while a background refresh runs. Requests never wait on the refresh.

Tag invalidation

Invalidatingts
await cache.set(`user:42`, data, { tags: ["users", "user:42"] });await cache.set(`user:43`, data, { tags: ["users", "user:43"] }); // Drops every key tagged "users" across L1 and L2await cache.invalidateTags(["users"]);

Tag the keys you can name and invalidate the group — you do not need to remember each individual key.

Consistency

L2 writes default to awaiting the disk, which is slower but means a read after a write always sees it. Set consistency: "async" to let writes land in the background.

tsts
await cache.set("k", v, { consistency: "async" });

Inspecting

Metricsts
const stats = cache.getMetrics();// { size, hits, misses, evictions, hitRate, … }

Durations

Anywhere a duration is accepted you can pass a number of milliseconds or a readable string.

tsts
"500ms"  "10s"  "5m"  "2h"  "7d"  "1w"   // strings500                                          // raw milliseconds

Global instance

If you do not want to thread a cache instance through your code, a configured default is available as a named export.

tsts
import { Cache } from "yatta/cache"; await Cache.set("k", v);const v = await Cache.get("k");
Warning
get returns unknown when no type parameter is given. Pass one — cache.get<User>("u:1") — so the result is usable.