Delete cache entry for user profile.

Our flush functions update user profile cache entries which can cause
confusing race conditions (see e.g. #1257).  To resolve this, we move
all the user_profile flush functions to delete the entry instead of
updating it -- it will then be fetched as part of the next request
that needs to access the user object.

There are still races here, and there is perhaps an argument that a
better fix for this would be to re-fetch the object and then put it
into the cache, but this resolves the main cache correctness problem
we had with the previous implementation.

Fixes: #1322.
This commit is contained in:
Umair Khan 2016-07-27 13:05:37 +05:00 committed by Tim Abbott
parent 1a6e8282c8
commit 8e5e6a06f2
1 changed files with 8 additions and 7 deletions

View File

@ -305,20 +305,21 @@ def get_stream_cache_key(stream_name, realm):
return u"stream_by_realm_and_name:%s:%s" % (
realm_id, make_safe_digest(stream_name.strip().lower()))
def update_user_profile_caches(user_profiles):
def delete_user_profile_caches(user_profiles):
# type: (Iterable[UserProfile]) -> None
items_for_remote_cache = {}
keys = []
for user_profile in user_profiles:
items_for_remote_cache[user_profile_by_email_cache_key(user_profile.email)] = (user_profile,)
items_for_remote_cache[user_profile_by_id_cache_key(user_profile.id)] = (user_profile,)
cache_set_many(items_for_remote_cache)
keys.append(user_profile_by_email_cache_key(user_profile.email))
keys.append(user_profile_by_id_cache_key(user_profile.id))
cache_delete_many(keys)
# Called by models.py to flush the user_profile cache whenever we save
# a user_profile object
def flush_user_profile(sender, **kwargs):
# type: (Any, **Any) -> None
user_profile = kwargs['instance']
update_user_profile_caches([user_profile])
delete_user_profile_caches([user_profile])
# Invalidate our active_users_in_realm info dict if any user has changed
# the fields in the dict or become (in)active
@ -345,7 +346,7 @@ def flush_realm(sender, **kwargs):
# type: (Any, **Any) -> None
realm = kwargs['instance']
users = realm.get_active_users()
update_user_profile_caches(users)
delete_user_profile_caches(users)
if realm.deactivated:
cache_delete(active_user_dicts_in_realm_cache_key(realm))