74 lines
2.9 KiB
Markdown
74 lines
2.9 KiB
Markdown
# @wrnexus/cache
|
|
|
|
Bounded in-memory/tag caching and HTTP response caching for WRNexusJS. Supports request deduplication, tag invalidation, ETags, fresh/stale states, and optional detached stale revalidation.
|
|
|
|
```ts
|
|
import { connectCacheInvalidation, TagCache, responseCache } from "@wrnexus/cache";
|
|
const cache = new TagCache({ ttlMs: 60_000, staleWhileRevalidateMs: 300_000 });
|
|
export default responseCache({ cache, tags: ["products"] });
|
|
```
|
|
|
|
`TagCache` bounds entries with LRU-style eviction, deduplicates concurrent
|
|
loaders, and prevents an invalidated in-flight loader from repopulating stale
|
|
data. Use `lookup()` when fresh/stale state matters, or `getOrLoad()` for
|
|
stampede-safe loading.
|
|
|
|
For multi-instance applications, connect the cache to any compatible pub/sub
|
|
bus (including `@wrnexus/pubsub`). Namespaces isolate applications sharing the
|
|
same broker. Local invalidation happens first and the returned promise confirms
|
|
cross-instance publication; failures remain visible to the caller.
|
|
|
|
```ts
|
|
import { connectCacheInvalidation, TagCache } from "@wrnexus/cache";
|
|
import { createPubSub } from "@wrnexus/pubsub";
|
|
import { redisDriver } from "@wrnexus/pubsub/redis";
|
|
|
|
const cache = new TagCache({ maxEntries: 10_000 });
|
|
const bus = createPubSub(redisDriver(process.env.REDIS_URL));
|
|
const invalidation = connectCacheInvalidation(cache, bus, {
|
|
namespace: "storefront-production",
|
|
onError: (error) => logger.error("cache invalidation failed", { error }),
|
|
});
|
|
|
|
await invalidation.invalidateTag("products");
|
|
await invalidation.delete("product:42");
|
|
|
|
// Unsubscribes this cache only; the shared bus remains owned by the app.
|
|
invalidation.close();
|
|
await bus.close();
|
|
```
|
|
|
|
## Framework cache layers
|
|
|
|
`CacheCoordinator` keeps the four cache lifetimes explicit:
|
|
|
|
- `coordinator.request()` creates request-only deduplication.
|
|
- `coordinator.data` caches loader/query results.
|
|
- `coordinator.component` caches reusable rendered fragments.
|
|
- `coordinator.page` caches complete safe documents.
|
|
|
|
All cross-request layers are bounded, tag-aware, stale-while-revalidate capable,
|
|
stampede-safe, and expose `withLock()` for exclusive per-key work. `inspect()`
|
|
returns metadata without cached values. Development applications expose that
|
|
inspection through the Cache panel and `GET /__wrnexus/cache`.
|
|
|
|
Pages and components can opt in declaratively:
|
|
|
|
```wrn
|
|
cache {
|
|
scope = "page"
|
|
strategy = "stale-while-revalidate"
|
|
ttl = "5m"
|
|
stale = "10m"
|
|
tags = ["catalog", "marketing"]
|
|
vary = ["tenant", "language"]
|
|
}
|
|
```
|
|
|
|
Omit `scope` to cache named loader data. Use `scope = "page"` for full-page
|
|
caching. Component policies cache their rendered fragment. Authenticated user
|
|
and tenant identities are always included automatically; page caches also vary
|
|
by language, theme, and accent. Add header names or `cookie:name` entries for
|
|
other application-specific variation. Pages containing CSRF forms are never
|
|
stored in the full-page cache.
|