Cache Plugins
withCachePrefix plugin
The Cache prefix plugin intercepts calls to a cache adapter and transparently prefixes all cache keys with a configurable string. This enables logical key namespacing without modifying the adapter implementation.
Use cases
- Multi-tenant systems — Prefix keys with a tenant identifier to isolate cache data between tenants
- Environment isolation — Separate development, staging, and production cache data
- Versioning — Prefix keys with a schema version for cache invalidation across deployments
- Module scoping — Organize cache keys by feature or module to avoid collisions
How it works
The withCachePrefix function returns a PluginFn that calls enhance on each adapter method that accepts a cache key. When an enhanced method is invoked, the plugin intercepts the call, prepends the configured prefix to the key argument, and forwards the modified arguments to the original method.
The plugin prefixes keys for the following methods:
| Method | Key argument | Pattern |
|---|---|---|
get | Second argument (key) | prefix + key |
getAndRemove | Second argument (key) | prefix + key |
add | Second argument (key) | prefix + key |
getOrAdd | Second argument (key) | prefix + key |
put | Second argument (key) | prefix + key |
update | Second argument (key) | prefix + key |
increment | Second argument (key) | prefix + key |
removeMany | Second argument (keys) | keys.map(k => prefix + k) |
removeByPrefix | Second argument (key) | prefix + key |
Methods that do not accept a key (removeAll) are unaffected.
Usage
import { withPlugin } from "eridu-tech/middleware";
import { MemoryCacheAdapter } from "eridu-tech/cache/memory-cache-adapter";
import { withCachePrefix } from "eridu-tech/cache/plugins";
const adapter = new MemoryCacheAdapter();
// Apply the prefix plugin to the adapter
const prefixedAdapter = withPlugin(adapter, withCachePrefix("tenant-42:"));
Before/after behavior
Before — Keys are stored as-is:
import { MemoryCacheAdapter } from "eridu-tech/cache/memory-cache-adapter";
const adapter = new MemoryCacheAdapter();
await adapter.get("user:123");
// -> looks up key "user:123"
After — Keys are automatically prefixed:
import { withPlugin } from "eridu-tech/middleware";
import { MemoryCacheAdapter } from "eridu-tech/cache/memory-cache-adapter";
import { withCachePrefix } from "eridu-tech/cache/plugins";
const adapter = new MemoryCacheAdapter();
// Apply the prefix plugin to the adapter
const prefixedAdapter = withPlugin(adapter, withCachePrefix("tenant-42:"));
await prefixedAdapter.get("user:123");
// -> looks up key "tenant-42:user:123"
Because withPlugin uses enhance under the hood, the same edge case applies: if one enhanced method internally calls another enhanced method via this, the middleware will apply twice. Be mindful of inter-method calls when applying plugins that enhance multiple methods on the same instance.
For more information about the withPlugin function and applying plugins to adapters, see the Middleware plugin documentation.
Multiple keys — removeMany
The removeMany method receives an array of keys. The plugin maps over the array, prefixing each entry:
import { withPlugin } from "eridu-tech/middleware";
import { MemoryCacheAdapter } from "eridu-tech/cache/memory-cache-adapter";
import { withCachePrefix } from "eridu-tech/cache/plugins";
const adapter = new MemoryCacheAdapter();
// Apply the prefix plugin to the adapter
const prefixedAdapter = withPlugin(adapter, withCachePrefix("tenant-42:"));
await prefixedAdapter.removeMany(["a", "b", "c"]);
// -> prefixedAdapter.removeMany(["tenant-42:a", "tenant-42:b", "tenant-42:c"])
withCacheJitter plugin
The Cache jitter plugin adds random jitter to TTL values on cache add and put operations. Applying jitter to TTLs helps prevent cache stampedes (thundering-herd problems) by staggering the expiration times of cache entries that were originally created with the same TTL.
Use cases
- Cache stampede prevention — Stagger cache entry expiry times to avoid multiple concurrent cache refreshes
- Load smoothing — Distribute cache refresh load across time rather than having it spike at predictable intervals
- Distributed systems — Reduce synchronized expiration across many cache nodes or instances
How it works
The withCacheJitter function returns a PluginFn that calls enhance on the add and put methods of the adapter. When either method is invoked, the plugin intercepts the call, applies a random jitter factor to the TTL, and forwards the modified arguments to the original method.
The jitter is calculated as a random percentage of the original TTL. For example, with the default defaultJitter of 0.2 (20 %), a TTL of 60 seconds will be randomly adjusted to somewhere between 48 and 72 seconds.
| Method | TTL argument | Behaviour |
|---|---|---|
add | Third argument | Applies random jitter to the TTL |
add | Third argument | Applies random jitter to the TTL |
getOrAdd | Third argument | Applies random jitter to the TTL |
Usage
import { withPlugin } from "eridu-tech/middleware";
import { MemoryCacheAdapter } from "eridu-tech/cache/memory-cache-adapter";
import { withCacheJitter } from "eridu-tech/cache/plugins";
const adapter = new MemoryCacheAdapter();
// Apply the jitter plugin to the adapter
const jitteredAdapter = withPlugin(adapter, withCacheJitter());
Settings
| Option | Type | Default | Description |
|---|---|---|---|
defaultJitter | number | 0.2 | The jitter factor as a ratio of the original TTL (e.g., 0.2 means ±20 %) |
Because withPlugin uses enhance under the hood, the same edge case applies: if one enhanced method internally calls another enhanced method via this, the middleware will apply twice. Be mindful of inter-method calls when applying plugins that enhance multiple methods on the same instance.
For more information about the withPlugin function and applying plugins to adapters, see the Middleware plugin documentation.
withCacheWriteLock plugin
The Cache write lock plugin acquires a distributed lock before executing mutating cache operations. It wraps write operations (add, put, update, increment, getAndRemove, removeMany) with a lock acquired via an ILockFactory, ensuring that concurrent writes to the same cache entry are serialised.
Use cases
- Concurrency control — Prevent race conditions when multiple processes write to the same cache key
- Distributed environments — Coordinate writes across multiple application instances
- Critical sections — Ensure exclusive access for read-modify-write operations like
incrementandupdate - Batch safety — Serialise operations on multiple keys in
removeMany
How it works
The withCacheWriteLock function returns a PluginFn that calls enhance on the selected mutating methods of the adapter. When an enhanced method is invoked, the plugin acquires a lock keyed by the cache key (or keys, for removeMany) before executing the operation. The lock is released automatically after the operation completes.
The lock key is derived directly from the cache key, ensuring that concurrent writes to the same cache entry are serialised while writes to different entries can proceed in parallel.
By default, all mutating methods are protected:
| Method | Lock key source | Behaviour |
|---|---|---|
add | Single key | Acquires lock for the key before adding |
getOrAdd | Single key | Acquires lock for the key before adding |
put | Single key | Acquires lock for the key before putting |
update | Single key | Acquires lock for the key before updating |
increment | Single key | Acquires lock for the key before incrementing |
getAndRemove | Single key | Acquires lock for the key before removing |
removeMany | Multiple keys | Acquires locks for each key sequentially |
Read-only methods (get, removeAll, removeByPrefix) are unaffected.
Usage
import { withPlugin } from "eridu-tech/middleware";
import { MemoryCacheAdapter } from "eridu-tech/cache/memory-cache-adapter";
import { withCacheWriteLock } from "eridu-tech/cache/plugins";
import { LockFactory } from "eridu-tech/lock";
import { MemoryLockAdapter } from "eridu-tech/lock/memory-lock-adapter";
const adapter = new MemoryCacheAdapter();
const lockFactory = new LockFactory({
adapter: new MemoryLockAdapter(),
});
// Apply the write lock plugin to the adapter
const lockedAdapter = withPlugin(adapter, withCacheWriteLock({ lockFactory }));
Settings
| Option | Type | Default | Description |
|---|---|---|---|
lockFactory | ILockFactory | (required) | A factory that creates named locks |
onlyMethods | Array<WithCacheWriteLockMethods> | ["getAndRemove", "add", "put", "update", "increment", "removeMany"] | The subset of methods to protect with a write lock |
Because withPlugin uses enhance under the hood, the same edge case applies: if one enhanced method internally calls another enhanced method via this, the middleware will apply twice. Be mindful of inter-method calls when applying plugins that enhance multiple methods on the same instance.
For more information about the withPlugin function and applying plugins to adapters, see the Middleware plugin documentation.
For more information about lock factories, see the Lock documentation.