Cache Tags
Cache tags allow you to group related cache entries and invalidate them together. This makes complex invalidation scenarios simple and error-free.
:::tip Two ways to tag a value You can attach tags to a cached value in two interchangeable ways:
// Fluent — useful when you already have a tagged instanceawait cache.tags(["users"]).set("user.1", user);
// Inline — terser when you're writing one value with known tagsawait cache.set("user.1", user, { tags: ["users"] });Both share the same tag index — invalidate them with cache.tags([...]).invalidate() regardless of which shape you used at write time. See Set Options.
:::
Why Tags Matter?
Section titled “Why Tags Matter?”Without Tags:
// When user updates profile, you need to manually track all cache keysawait cache.remove("users.123.profile");await cache.remove("users.123.posts");await cache.remove("users.123.comments");await cache.remove("feed.homepage"); // if user is featured// Easy to miss keys, error-prone, hard to maintainWith Tags:
// One line invalidates everythingawait cache.tags([`user.123`]).invalidate();// All related cache entries cleared automaticallyPrimary Example: User Profile Updates
Section titled “Primary Example: User Profile Updates”When a user updates their profile, invalidate all related cache with a single operation:
import { cache, CACHE_FOR } from "@warlock.js/cache";
// Store user data with tagsconst userId = 123;const userCache = cache.tags(["user", `user.${userId}`]);
await userCache.set(`user.${userId}.profile`, profileData, CACHE_FOR.ONE_HOUR);await userCache.set(`user.${userId}.preferences`, settingsData, CACHE_FOR.ONE_HOUR);
// When user updates their profileasync function updateUserProfile(userId: number, data: any) { await db.users.update(userId, data);
// Invalidate all user-related cache await cache.tags(["user", `user.${userId}`]).invalidate();}All keys stored with these tags are invalidated together. Any cache entries tagged with "user" or user.${userId} are removed immediately.
E-Commerce Examples
Section titled “E-Commerce Examples”Cart Cache with User Tags
Section titled “Cart Cache with User Tags”import { cache, CACHE_FOR } from "@warlock.js/cache";
const userId = 123;const cartCache = cache.tags(["cart", `user.${userId}`]);
// Store cart itemsawait cartCache.set(`cart.${userId}.items`, cartItems, CACHE_FOR.HALF_HOUR);await cartCache.set(`cart.${userId}.total`, cartTotal, CACHE_FOR.HALF_HOUR);
// Invalidate cart when user updates itawait cache.tags([`user.${userId}`]).invalidate();Product Cache with Category Tags
Section titled “Product Cache with Category Tags”const categoryId = 5;const productCache = cache.tags(["products", `category.${categoryId}`]);
await productCache.set(`products.category.${categoryId}`, products, CACHE_FOR.ONE_HOUR);await productCache.set(`products.featured.${categoryId}`, featured, CACHE_FOR.ONE_HOUR);
// Invalidate all products in category when catalog updatesawait cache.tags([`category.${categoryId}`]).invalidate();Complex Tagging Patterns
Section titled “Complex Tagging Patterns”Multi-dimensional User + Product Tags
Section titled “Multi-dimensional User + Product Tags”import { cache, CACHE_FOR } from "@warlock.js/cache";
// User wishlists tagged by user and productsconst userId = 123;const productId = 456;
const wishlistCache = cache.tags([ `user.${userId}`, `product.${productId}`, "wishlists"]);
await wishlistCache.set( `wishlist.${userId}.${productId}`, wishlistData, CACHE_FOR.ONE_WEEK);
// Invalidate by user (clear all user's wishlists)await cache.tags([`user.${userId}`]).invalidate();
// OR invalidate by product (clear all wishlists containing this product)await cache.tags([`product.${productId}`]).invalidate();
// OR invalidate all wishlistsawait cache.tags(["wishlists"]).invalidate();Tag Patterns and Best Practices
Section titled “Tag Patterns and Best Practices”Use Hierarchical Tags
Section titled “Use Hierarchical Tags”// Good: Hierarchical tagscache.tags([`user.${id}`, "users"]);
// Good: Resource-based tagscache.tags([`post.${postId}`, "posts", `category.${categoryId}`]);
// Avoid: Overly specific tags that duplicate key informationcache.tags([`user.${id}.profile`, `user.${id}.posts`]); // Too specificCombine Multiple Tag Levels
Section titled “Combine Multiple Tag Levels”const tagged = cache.tags([ `user.${userId}`, // User-specific "users", // All users `category.${catId}`, // Category-specific "posts" // All posts]);
// When user updates: invalidate user-specific cacheawait cache.tags([`user.${userId}`]).invalidate();
// When category updates: invalidate category-specific cacheawait cache.tags([`category.${catId}`]).invalidate();
// When all posts need refresh: invalidate all postsawait cache.tags(["posts"]).invalidate();Tagged Cache Operations
Section titled “Tagged Cache Operations”All standard cache operations work with tagged cache:
import { cache } from "@warlock.js/cache";
const tagged = cache.tags(["users"]);
// Getconst user = await tagged.get("user.123");
// Setawait tagged.set("user.123", userData, 3600);
// Remove (removes key and tag relationships)await tagged.remove("user.123");
// Hasconst exists = await tagged.has("user.123");
// Remember (with stampede prevention)const user = await tagged.remember("user.123", 3600, async () => { return await fetchUser(123);});
// Pull (get and remove)const temp = await tagged.pull("temp.data");
// Forever (no expiration)await tagged.forever("config", configData);
// Increment/Decrementawait tagged.increment("user.123.views");Performance Considerations
Section titled “Performance Considerations”How Tags Work Internally
Section titled “How Tags Work Internally”- When you store a key with tags, the cache also stores tag-to-key relationships
- Each tag maintains a list of all keys associated with it
- When you invalidate tags, the cache looks up all keys for those tags and removes them
- Tag relationship keys are stored permanently (no TTL)
Performance Impact
Section titled “Performance Impact”- Storage: Minimal overhead - one additional key per tag per unique key
- Invalidation: Efficient - O(n) where n is the number of keys per tag
- Reads: No performance impact - tagged cache reads are as fast as regular cache
- Memory: Tag relationships stored in cache (uses same driver)
Best Practices for Performance
Section titled “Best Practices for Performance”- Don’t over-tag: Use 2-4 tags per cache entry, not 10+
- Use specific tags:
user.123is better thanuserswhen invalidating one user - Group invalidation: Invalidate multiple tags together when possible
- Monitor tag counts: If a tag has thousands of keys, invalidation will take longer
Limitations
Section titled “Limitations”- Tag relationships stored in cache: Uses the same driver as your cache data
- No tag hierarchy: Tags are flat (no parent-child relationships)
- Synchronous tag lookup: Tag relationships must be readable for invalidation to work
Troubleshooting
Section titled “Troubleshooting”- Tags not invalidating? Ensure you’re using the same tag names when storing and invalidating
- Performance issues? Check if tags have too many keys associated — consider using more specific tags
- Missing keys after invalidation? Tags invalidate based on stored relationships — ensure keys were stored through tagged cache instances
See Best Practices for more tag organization strategies.