Miscellaneous¶
This section talks about many things.
CacheMeta¶
If you are too lazy to create ModelCacheManager class for a model in <some_app>/caches.py, then you can just create CacheMeta class inside your model and define attributes like get_key_fields_list, filter_key_fields_list etc.
E.g.
class Event(models.Model):
slug = models.CharField(...)
title = models.CharField(...)
# other fields
class CacheMeta:
get_key_fields_list = [
('id',),
('slug',),
]
# Then you can use cache queries
event = Event.cache.get(id=event_id)
# or
event = Event.cache.get(slug=event_slug)
If both ModelCacheManager and CacheMeta are defined for a model then attributes defined in both classes gets merged.
Cached ForeignKey¶
Consider this model Participation which foreignkeys to model Event.
class Participation(models.Model):
event = models.ForeignKey(Event)
user = models.ForeignKey(User)
# other fields
And you’ve got an instance of Participation say participation, then
accessing event on it by participation.event will make a db query.
But we know that we can get event using cache call simply if we have id of it.
To make foreignkeys to use memcache call instead of db query, put in all names
of such fields in cached_foreignkeys in CacheMeta or ModelCacheManager.
E.g. change above code to
class Participation(models.Model):
event = models.ForeignKey(Event)
user = models.ForeignKey(User)
# other fields
class CacheMeta:
cached_foreignkeys = ['event', 'user']
# Now accessing event on participation will make cache call instead of
# db query
event = participation.event
Note: cached_foreignkeys will work only if related model has cache class
registered (by InstanceCache or get_key_fields_list) on it’s primary key and
get_instance method is not overridden.
Timeout¶
timeout attribute can be put on all types of cache classes and
ModelCacheManager. Timeout is number of seconds after which memcached will
make the value expired. By default it is a week.
E.g.
from flash.constants import CACHE_TIME_DAY
class UserCacheOnUsername(InstanceCache):
model = User
key_fields = ('username',)
timeout = CACHE_TIME_DAY
Static version¶
All cache classes have static versions associated with them, which are concatenated while creating keys. Version should be an integeral value and by default it’s Zero (0). So increment the version every time you change the logic of getting value using fallback method.
E.g.
class ParticipatedEventIdListCacheOnUser(QuerysetCache):
model = Participation
key_fields = ('user',)
version = 2
def get_result(self, user):
event_ids = Participation.objects.filter(
user=user).values_list('event', flat=True)
return event_ids
Note: Do not just bump this static version if some migrations are made on some model.
Dynamic version¶
All values which are saved in memcached against keys are wrapped in a special class along with a timestamp at that time and a dynamic version of their cache class. So when some new fields are added/modified/deleted in a model, you just need to bump the dynamic version of all cache classes associated with that model.
E.g. You made some changes in fields of Event model, then after the migrations are done and code changes have been deployed, do this in shell.
from flash.models import CacheDynamicVersion
CacheDynamicVersion.objects.bump_version_for_model(Event)
Invalidation¶
Flash handles invalidation automatically by default.
Each cache class has an invalidation type associated with it.
It can be set by giving one of the follwoing values to attribute
invalidation inside cache class.
- InvalidationType.OFF
No automatic invalidation will happen.
- InvalidationType.UNSET
It’s the default. Whenever some instance.save(), instance.delete(), queryset.update() or queryset.delete() happens, corresponding keys to instances which get changed are deleted from memcached.
Next time cache query happens, key won’t be found in memcached then it will get set in memcached after getting the value from db.
- InvalidationType.DYNAMIC
Values get invalidated dynamically. When a value is fetched it’s checked whether it is stale or not by checking associated key.
Allowtime¶
If a db query is expensive and write is heavy on some model so that cache is getting invalidated very frequently, then you may get okay with serving stale data for some time (let’s say for few seconds to minutes).
You may come up with the solution of making invalidation OFF and putting the timeout little. But this costs you even when there is no change in your model and db query happens everytime after the timeout.
So instead of doing this you can put allowtime attribute and make
invalidation DYNAMIC. It will allow the value to be stale for given time but
also only invalidate it if it’s needed.
Manual Invalidation¶
Sometimes it may happen (due to misconfiguration maybe) that some cache query’s
value gets inconsistent against some instances of a model and you want to
invalidate them, then use invalidate_flash_cache() method on the
queryset.
# Make a queryset conatining all those instances
qs = YourModel.objects.filter(...)
# and then do this
qs.invalidate_flash_cache()