@plakboek/cache
v0.6.0
Published
Cache tags, the cache backend contract and a bounded in-process cache for Plakboek CMS installations
Readme
@plakboek/cache
Cache tags, the cache backend and invalidator contract, and a bounded
in-process cache for Plakboek CMS installations. The visitor request handler
in @plakboek/render reads and fills a CacheBackend; the page engine in
@plakboek/pages purges a CacheInvalidator after a write commits. This
package holds the contract both sides are written against, plus one
implementation. It has no runtime dependency.
Install
pnpm add @plakboek/cacheStatus
The tag vocabulary, the backend contract and the bounded memory cache are
implemented and exported below. A shared backend (Redis, a CDN) is a later
implementation of the same CacheBackend shape.
Ordering contract
Three rules keep a cache from serving stale content:
- Take
ticket()before reading any data you will cache. setresolvesfalseand stores nothing when any of its tags (or theglobaltag) was purged after that ticket was taken, so a fill that raced a purge is dropped instead of resurrecting stale content.- A purge runs after the writing transaction commits, never before.
Process-local
createMemoryCache lives in one process. Two server processes behind a proxy
each hold their own cache and do not share purges, so a purge in one does not
reach the other until its entries expire (ttlMs, ten minutes by default).
Run a single process, or put a shared backend behind the same contract.
Layers
The in-process LRU is one cache layer. A reverse proxy or a CDN in front of
the server, added when the deployment is decided, is another. A writer holds
one CacheInvalidator; composeInvalidators(lru, proxy) returns one that
purges every layer in parallel. A layer that fails never stops the others:
the composed purge still calls every layer and then rejects with a
CachePurgeError carrying each failure, which the page engine reports after
the publish has committed instead of failing it.
Public API
Every export of @plakboek/cache, grouped the way src/index.ts groups
them. tests/unit/public-api.test.ts compares these tables with the entry
point, so an export cannot be added or removed without updating them.
Tags
| Export | Kind | Purpose |
| ---------------------- | -------- | ---------------------------------------------------------------- |
| GLOBAL_TAG | constant | global: purging it removes every entry |
| CACHE_TAG_PATTERN | constant | The pattern every well-formed tag matches |
| pageTag | function | Builds page:<id> |
| contentTypeTag | function | Builds content-type:<key> |
| menuTag | function | Builds menu:<key> |
| isCacheTag | function | Whether a value is a well-formed tag |
| InvalidCacheTagError | class | Thrown by a tag helper when its input would not form a valid tag |
Backend contract
| Export | Kind | Purpose |
| ------------------ | ---- | ------------------------------------------------------------------- |
| CacheEntry | type | One cached response: body, etag, language, manifest hash, build id |
| CacheSetOptions | type | The tags a fill depends on and the ticket taken before its read |
| CacheInvalidator | type | The write side: purge(tags) |
| CacheBackend | type | An invalidator plus ticket(), get(key) and set(key, entry, o) |
Layers
| Export | Kind | Purpose |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| composeInvalidators | function | Fans one purge out to every layer in parallel; one failure never stops the rest |
| purgePageTags | function | Purges the page tag of each page id; an empty list makes no call |
| CachePurgeError | class | Thrown by a composed purge when any layer failed; failures holds each error, failedLayers the position of each failed layer |
Memory cache
| Export | Kind | Purpose |
| ---------------------------------- | -------- | ---------------------------------------------------------------- |
| createMemoryCache | function | A bounded in-process LRU cache with a tag index and purge epochs |
| MemoryCacheConfigError | class | Thrown by createMemoryCache with every invalid option |
| DEFAULT_MEMORY_CACHE_MAX_ENTRIES | constant | 1000: the default entry-count bound |
| DEFAULT_MEMORY_CACHE_MAX_BYTES | constant | 67108864 (64 MiB): the default byte bound |
| DEFAULT_MEMORY_CACHE_TTL_MS | constant | 600000 (10 minutes): the backstop expiry |
| MemoryCacheOptions | type | Input to createMemoryCache: the bounds and an injectable clock |
