cache: Only cache list results of QuerySets, not the QuerySet itself.

Storing a QuerySet rather than the list version of the result in it
has a large overhead -- and, as noted by the type annotations, the
result is only ever used as a list.  This difference is particularly
important because the cached `get_realm_user_dicts` can get extremely
large for realms with large numbers of users, potentially overflowing
the 1MB default object limit in memcached.

Switch all cases of `cache_with_key` which return QuerySets to
returning the list values of them.
This commit is contained in:
Alex Vandiver 2022-10-12 11:22:57 -04:00 committed by Tim Abbott
parent 204f1b58e8
commit 55c0e670d9
1 changed files with 6 additions and 4 deletions

View File

@ -3825,9 +3825,11 @@ def get_user_by_id_in_realm_including_cross_realm(
@cache_with_key(realm_user_dicts_cache_key, timeout=3600 * 24 * 7)
def get_realm_user_dicts(realm_id: int) -> List[Dict[str, Any]]:
return UserProfile.objects.filter(
realm_id=realm_id,
).values(*realm_user_dict_fields)
return list(
UserProfile.objects.filter(
realm_id=realm_id,
).values(*realm_user_dict_fields)
)
@cache_with_key(active_user_ids_cache_key, timeout=3600 * 24 * 7)
@ -3879,7 +3881,7 @@ def get_source_profile(email: str, realm_id: int) -> Optional[UserProfile]:
@cache_with_key(bot_dicts_in_realm_cache_key, timeout=3600 * 24 * 7)
def get_bot_dicts_in_realm(realm: Realm) -> List[Dict[str, Any]]:
return UserProfile.objects.filter(realm=realm, is_bot=True).values(*bot_dict_fields)
return list(UserProfile.objects.filter(realm=realm, is_bot=True).values(*bot_dict_fields))
def is_cross_realm_bot_email(email: str) -> bool: