bentocache icon indicating copy to clipboard operation
bentocache copied to clipboard

Tagging system

Open Julien-R44 opened this issue 11 months ago • 7 comments

Creating an issue in case people have the same question.

I am not fully happy with the namespace system we have. Not enough for easy invalidation. So I would love to have tags and it's planned, but it's quite a tricky thing to nail down, especially in bentocache, where we have a lot of different features

I've been doing quite a bit of research on the topic and should try an implementation when I have some time. The API will be super simple:

await bento.getOrSet({
  key: 'foo',
  factory: getFromDb(),
  tags: ['tag-1', 'tag-2']
});

await bento.set({ 
  key: 'foo',
  tags: ['tag-1']
});

Then, we could delete all entries tagged with tag-1 using:

await bento.delete({ tag: 'tag-1' });

This would remove all entries that have the tag-1 tag.

It's likely that this tagging system will deprecate the current namespace system we have. I think they would become useless once we have tags. We would keep namespaces until v2, but they would be marked as deprecated via JSDoc

Julien-R44 avatar Feb 11 '25 00:02 Julien-R44

Super happy to see this issue. Allow me to propose a mechanism that I believe is universal for any kind o production system: https://github.com/mcollina/async-cache-dedupe#invalidation.

It's actually quite similar to the tags you illustrated, except that in the proposed solution the tags can be computed based on the factory result.

Suppose I have a Product model that can be assigned to many Categories. I then do a query for Product:1 that is currently assigned to ['Category:1', 'Category:2']. I then mark Category:2 as archived in other part of the system, and I'd like to invalidate all Product queries/caches that referenced the Category:1.

That's impossible to tell with static tags without the context of factory result. But from my understanding, the difficult part is tag-based invalidation. Collecting tags (or references) from a callback instead of array shouldn't present too many complications?

wodCZ avatar Feb 11 '25 11:02 wodCZ

Yup absolutely, I should have included it in my first comment. In fact, the tags will be compatible with the adaptive caching system we have today. So :

const product = await bento.getOrSet({
  key: 'product:1',
  factory: async (ctx) => {
     const product = await getFromDb(1)

     ctx.setTags(product.categories.map((category) => category.name))

     return product
  }
});

Lemme know if it could suit you

Julien-R44 avatar Feb 11 '25 11:02 Julien-R44

Beautiful 😍 I believe that solves my last trouble with the library, and would make bentocache the ultimate caching library for production-grade node apps!

In fact, such API should be sufficient to implement an automatic ORM caching layer similar to https://github.com/Suor/django-cacheops. Basically, all relations returned by the ORM are saved as references, and all updates/deletes automatically invalidate relevant results. That's definitely something out of scope of bentocache itself, but could provide very solid ground for a 3rd party integration.

Possibly, ctx.addTags would be useful to make it easier to collect tags from multiple helpers, but I guess that could be achieved with ctx.setTags([...ctx.tags, ...result.otherRelations.map(r => r.id)]).

wodCZ avatar Feb 11 '25 11:02 wodCZ

I merged PR #57, which adds experimental support for tags 🎉 Experimental, because the feature is pretty complex. So I would really like to have some feedbacks and use it in some real world projects to be more confident about it

Next steps:

  • ~~Add a new docs page on it, because the internals of the tag system, the way it works, is not ultra-conventional. This needs to be documented. Also add a warning that tags are experimental and ideally require feedback~~
    • Docs available : https://bentocache.dev/docs/tagging
  • ~~Release [email protected] with tags~~
  • Keep this issue open to track feedback on tags

Still not sure if we should deprecated namespace. Lemme know if you have any thoughts

Also @wodCZ and @dunhamjared thanks a lot for the sponsorship 🙏

Julien-R44 avatar Feb 16 '25 02:02 Julien-R44

1.2.0 released with docs

Release notes : https://github.com/Julien-R44/bentocache/releases/tag/bentocache%401.2.0 Docs : https://bentocache.dev/docs/tagging

If you encounter any bugs with tagging or have any feedback, please let me know!

Julien-R44 avatar Feb 16 '25 21:02 Julien-R44

Hello @Julien-R44

There's a bug with .has method

After deleting by tag, .has method stills return true

	const key = 'user:1';
	const tags = ['userTag'];

	await bentoCache.set({
		key,
		value: 'lol',
		tags,
	});

	await bentoCache.deleteByTag({ tags });

	await bentoCache.has({ key }); // This returns true

Maged-Zaki avatar Apr 13 '25 03:04 Maged-Zaki

@Maged-Zaki Thanks, indeed a bug. Fixed by #65

Julien-R44 avatar Apr 20 '25 15:04 Julien-R44

now that it's fixed, we should close this @Julien-R44

thisisnkc avatar Dec 18 '25 08:12 thisisnkc