TagCache class

Tagged cache over redis: values are stored under their own keys, and each key is additionally added to a redis set per tag. Invalidating a tag then drops every value that was stored with it, which is what plain key-based caching cannot express — one write can be invalidated by any of several unrelated events.

The typical use is caching a computed result that depends on several entities and dropping it when any one of them changes:

import { RedisCache } from '@imqueue/rpc';
import { TagCache } from '@imqueue/tag-cache';

const cache = new TagCache(await new RedisCache().init({ prefix: 'app' }));

await cache.set('user:1:invoices', invoices, ['user:1', 'invoices'], 60000);

// later, when user 1 changes — drops the entry above and anything else
// tagged 'user:1', whatever key it was stored under
await cache.invalidate('user:1');

Two things to know before relying on it. Read and write operations do NOT throw on a redis failure: they log a warning and report the failure in their return value, so a cache outage degrades to cache misses instead of taking the caller down. And the underlying redis connection is shared and owned by RedisCache, so TagCache.destroy() tears it down for every instance — see that method.

Signature:

export declare class TagCache 

Constructors

Constructor

Modifiers

Description

(constructor)(cache)

Constructs a new instance of the TagCache class

Properties

Property

Modifiers

Type

Description

cache?

RedisCache | undefined

(Optional) The RedisCache this instance borrows its connection, key prefix and logger from. Deleted by TagCache.destroy(). Documented here rather than above the constructor because it is a parameter property, and that is the only place a doc comment reaches the emitted declaration.

key

readonly

(key: string) => string

Maps a caller-supplied key onto the fully-qualified redis key, applying the prefix the underlying RedisCache was initialised with. Bound to that cache, so it is safe to pass around detached.

logger

ILogger

Logger inherited from the underlying RedisCache. Every swallowed redis error is reported through it at warning level.

redis?

Redis

(Optional) Shared ioredis connection taken from RedisCache at construction time. Absent until RedisCache.init() has been awaited, and deleted again by TagCache.destroy() — while it is absent every operation throws a TypeError carrying REDIS_INIT_ERROR.

Methods

Method

Modifiers

Description

destroy()

Destroys this cache instance

Note the connection is owned by RedisCache and shared, so this closes it for **every** consumer, not just this instance — including other TagCache objects built from the same cache. Treat it as application shutdown rather than as releasing one instance.

Afterwards this instance keeps no redis reference, so every operation on it throws a TypeError carrying REDIS_INIT_ERROR.

get(keys)

Returns data stored under given keys. If a single key provided returns a single result, otherwise it will return an array of results associated with the keys

Values are JSON-decoded on the way out, so what comes back is what was passed to TagCache.set(), not a string.

A redis failure is not thrown: it is logged as a warning and reported as null. That makes null ambiguous between "not cached" and "lookup failed", which is the right trade for a cache but means it must never be treated as proof that a value is absent.

invalidate(tags)

Invalidates data under given tags

Collects every key held by the given tags, deletes those keys, and then removes them from all other tag sets so no tag is left pointing at a key that no longer exists.

Two properties worth knowing, because neither is obvious from the signature:

  • **It resolves before the work is confirmed.** The deletion is dispatched as a MULTI whose result is not awaited — a failure is logged, not returned. So a true result means "the invalidation was issued", not "the keys are gone". Do not use it to order a subsequent read. - **The cleanup pass scans every tag**, not just the ones passed in, since a key may be held by tags other than those being invalidated. Cost therefore grows with the total number of tags in the keyspace rather than with the size of tags.

set(key, value, tags, ttl)

Stores given value under a given key, tagging it with the given tags

The value is JSON-encoded, and the key is added to one redis set per tag so TagCache.invalidate() can find it later. Everything happens in a single MULTI, so a value is never visible without its tag membership.

When ttl is given it is applied to the value AND refreshed on each tag set, so tag sets do not outlive the entries they track. Without it, nothing expires and the entry lives until it is invalidated.

Read this page as plain markdown — no HTML, no navigation. For pasting into an LLM, or for an agent to fetch.