'Using cache invalidation for a user permission django
I am currently working on a cache for user permissions in Django.
The structure for enabling users/organizations access to certain areas of the application looks like this:

Meaning a User can either have access to a feature via his organization or as a user himself. What I want is a key-value cache that safes all the features a user/organization member has access to as a string. So whenever a user request access to a certain view the permission class first fetches all features for that user from the cache. I have implemented that as follows:
class UserPermissionCache:
def __init__(self):
self.redis = StrictRedis(
host=settings.REDIS_RATE_LIMITING_URL,
port=settings.REDIS_RATE_LIMITING_PORT,
db=settings.REDIS_RATE_LIMITING_DB,
)
self.validation = datetime.now().strftime("%m%d%Y%H%M%S")
def status_key(self, member):
return f"user:features:{self.validation}:{member.uuid}"
def set_user_features(self, user, key):
features = Feature.objects.filter(
Q(enabled_for_all=True)
| Q(enabled_for_users=user)
| Q(enabled_for_organizations=user.only_organization)
)
result = FEATURE_STRING_SEPERATOR.join(
feature.code for feature in features
)
if not self.redis.set(key, result, settings.USER_FEATURES_TIMEOUT):
logging.warning(f"Couldn't set features for user: {user.uuid}")
return result
def get_user_features(self, member):
key = self.status_key(member)
try:
result = self.redis.get(key)
except ConnectionError:
logger.error(
"ConnectionError, Failed to fetch feature permission for user",
extra={"member": member.uuid},
)
return None
if result is None:
result = self.set_user_features(member, key)
result = result.split(FEATURE_STRING_SEPERATOR)
else:
result = result.decode("utf-8").split(FEATURE_STRING_SEPERATOR)
return result
def reset_user_features(self, sender, **kwargs):
for member in organization_members:
key = self.status_key(member)
self.redis.delete(key)
def update_validation_key(self, sender, instance, **kwargs):
self.validation = datetime.now().strftime("%m%d%Y%H%M%S")
user_permission_cache = UserPermissionCache()
m2m_changed.connect(user_permission_cache.reset_user_features, sender=Feature.enabled_for_organizations.through)
post_save.connect(user_permission_cache.update_validation_key, sender=Feature)
My Problem is now cache validation. I have the set the timeout for an entry in the cache to 15 min. Whenever I want to add features to organizations or change a feature as an admin I want the cache to update as well so every user gets immediate access. I tried to solve this by adding a time to the key. Whenever a Feature gets updated I change the key as well which means that the cache cant find the old key-value pair anymore and will just create a new entry with the updated feature. I just dont know what to do whenever a view is called to add features to an organization. For that I use the m2m_changed Signal. I was hoping there is a better solution then this.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
