Invalidations
Invalidation is the hard part of caching. It’s easy to stash data away for a rainy day, but it’s hard to know when the sun comes out and that stashed data is no-longer valid. While t87s can’t help you know when to invalidate things, it allows you to do it with pizzazz.
Basic invalidation
Section titled “Basic invalidation”// Invalidate one userawait cache.invalidate(cache.tags.users('123'));
// Invalidate ALL users (nuclear option)await cache.invalidate(cache.tags.users);
// Invalidate a specific postawait cache.invalidate(cache.tags.users('123').posts('p1'));# Invalidate one userawait cache.invalidate(cache.t.users("123"))
# Invalidate ALL users (nuclear option)await cache.invalidate(cache.t.users)
# Invalidate a specific postawait cache.invalidate(cache.t.users("123").posts("p1"))When you invalidate a tag, everything under that tag goes stale too. Invalidating users('123') also invalidates users('123').posts('p1') and users('123').posts('p1').comments('c9') and so on. This is called prefix matching, and it’s almost always what you want.
If you’re updating a user, you probably want their cached posts and comments to refresh too—they might depend on the user’s name or avatar or some other field that just changed. t87’s prefix matching handles this automatically. And it’s how the vast majority of caching libraries that t87s ripped off handle this as well.
Exact invalidation
Section titled “Exact invalidation”Sometimes you don’t want the cascade. Maybe you updated a user’s email but their posts and comments are genuinely unaffected. In that case, you can do an exact invalidation:
// Only invalidate users:123, NOT users:123:posts:*await cache.invalidate(cache.tags.users('123'), true);
// Or via primitives:await cache.primitives.invalidate([['users', '123']], { exact: true });# Python support for exact invalidation is coming soon.# For now, use prefix invalidation, which is the safer default anyway.await cache.invalidate(cache.t.users("123"))Be careful with exact invalidation. It’s an optimization, and like most optimizations (except caching), it’s often not necessary. When in doubt, let the prefix cascade happen.
When to invalidate
Section titled “When to invalidate”In a caching system, you should invalidate data when the data changes. But where you put that invalidation call matters.
After writes, not before. You want to invalidate after the database write succeeds, not before. If you invalidate first and then the write fails, you’ve just cleared good data for no reason.
Close to the mutation. Put your invalidation calls near the code that mutates the data. If updateUser() changes the database, it should also invalidate the cache. Don’t scatter invalidations across unrelated files where you’ll forget about them.
Consider side effects. Sometimes updating one thing affects another. If updating a user’s name should also invalidate their comments (because comments display the author name), make sure that invalidation happens. This is the part people forget.
I’m not going to insult your intelligence with even more AI-generated bullet points than the three you read above (jk, AI didn’t generate them—it generated this paragraph though… joke’s on you…). But hopefully you get the point—this is the hard part of caching. And the whole reason t87s was built is not because a lib magically fixes it, but because LLMs are so powerful that, fed the right data, they can correct a cache based on historical data in near real-time.