Skip to main content

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:

MethodKey argumentPattern
getSecond argument (key)prefix + key
getAndRemoveSecond argument (key)prefix + key
addSecond argument (key)prefix + key
getOrAddSecond argument (key)prefix + key
putSecond argument (key)prefix + key
updateSecond argument (key)prefix + key
incrementSecond argument (key)prefix + key
removeManySecond argument (keys)keys.map(k => prefix + k)
removeByPrefixSecond argument (key)prefix + key

Methods that do not accept a key (removeAll) are unaffected.

Usage​

./samples/with-cache-prefix.ts
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:

./samples/unprefixed-lookup.ts
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:

./samples/prefixed-lookup.ts
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"
danger

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.

info

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:

./samples/remove-many-prefix.ts
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.

MethodTTL argumentBehaviour
addThird argumentApplies random jitter to the TTL
addThird argumentApplies random jitter to the TTL
getOrAddThird argumentApplies random jitter to the TTL

Usage​

./samples/with-cache-jitter.ts
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​

OptionTypeDefaultDescription
defaultJitternumber0.2The jitter factor as a ratio of the original TTL (e.g., 0.2 means ±20 %)
danger

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.

info

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 increment and update
  • 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:

MethodLock key sourceBehaviour
addSingle keyAcquires lock for the key before adding
getOrAddSingle keyAcquires lock for the key before adding
putSingle keyAcquires lock for the key before putting
updateSingle keyAcquires lock for the key before updating
incrementSingle keyAcquires lock for the key before incrementing
getAndRemoveSingle keyAcquires lock for the key before removing
removeManyMultiple keysAcquires locks for each key sequentially

Read-only methods (get, removeAll, removeByPrefix) are unaffected.

Usage​

./samples/with-cache-write-lock.ts
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​

OptionTypeDefaultDescription
lockFactoryILockFactory(required)A factory that creates named locks
onlyMethodsArray<WithCacheWriteLockMethods>["getAndRemove", "add", "put", "update", "increment", "removeMany"]The subset of methods to protect with a write lock
danger

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.

info

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.