Suor / django-cacheops

A slick ORM cache with automatic granular event-driven invalidation.
BSD 3-Clause "New" or "Revised" License
2.11k stars 227 forks source link

Serve stale data instead of blocking with `lock=True` #289

Open mrmachine opened 6 years ago

mrmachine commented 6 years ago

@cached_as(..., lock=True) can be used to avoid dog-pile effect, but this still results in slow queries as clients wait to acquire lock while cache is being populated (but at least results in lower resource utilisation).

I'd like an option to immediately serve stale data (if available) from the cache instead of blocking while the cache is being populated. This was mentioned in https://github.com/Suor/django-cacheops/pull/134#issuecomment-76676304 as the second point of avoiding dog-pile.

Can you give some advice on how to approach this implementation in django-cacheops?

Suor commented 6 years ago

Now cacheops immediately erases data on event, so you can't serve anything stale. Need to change this first.

And there could be lots of strategies with that. You can move it, mark it stale or add to invalidated list. This depends on how you want to work it later.

mrmachine commented 6 years ago

@Suor thanks for the feedback. So something like this:

If I'm reading correctly, this should:

Suor commented 6 years ago

This could work. Have you tried implementing it?

Also, volatile keys are expired and deleted even without any max-memory setting.

mrmachine commented 6 years ago

@Suor Thanks for the feedback on the approach. I have not had a chance to try and implement it yet. I have currently just backported dogpile mitigation (locking) to an old version of django-cacheops for use in a Django 1.6 project. That is working to reduce load but is not improving response times. Time is now just spent with most requests waiting for the locks to release instead of recreating cache data in parallel. Next I will try to implement the above, and hopefully with minor tweaks it can be adapted for the current version of django-cacheops too, assuming it works.

Suor commented 1 year ago

This should be easier to do in new insideout mode. That ones doesn't drop the cache key on invalidation but only removes conj stamps.

Suor commented 1 year ago

I will leave a hint in case anyone wants to champion this:

  1. In INSIDEOUT mode it should be pretty easy to add a flag like allow_stale to @getset.getting() and ignore stamps if that is passed.
  2. Add the same flag to @cached_as(), QuerySet.cache() and even CACHEOPS setting's dicts. Pass it down.