perf: Extract get_subscribed_stream_ids_for_user.

This new method prevents us from getting fat
objects from the database.

Instead, now we just get ids from the database
to build our subqueries.

Note that we could also technically eliminate
the `set(...)` wrappers in this code to have
Django make a subquery and save a round trip.
I am postponing that for another commit (since
it's still somewhat coupled to some other
complexity in `do_get_streams` that I am trying
to cut through, plus it's not the main point
of this commit.)

BEFORE:

    # old, still in use for other codepaths
    def get_stream_subscriptions_for_user(user_profile: UserProfile) -> QuerySet:
        # TODO: Change return type to QuerySet[Subscription]
        return Subscription.objects.filter(
            user_profile=user_profile,
            recipient__type=Recipient.STREAM,
        )

    user_subs = get_stream_subscriptions_for_user(user_profile).filter(
        active=True,
    ).select_related('recipient')
    recipient_check = Q(id__in=[sub.recipient.type_id for sub in user_subs])

AFTER:

    # newly added
    def get_subscribed_stream_ids_for_user(user_profile: UserProfile) -> QuerySet:
        return Subscription.objects.filter(
            user_profile_id=user_profile,
            recipient__type=Recipient.STREAM,
            active=True,
        ).values_list('recipient__type_id', flat=True)

    subscribed_stream_ids = get_subscribed_stream_ids_for_user(user_profile)
    recipient_check = Q(id__in=set(subscribed_stream_ids))
This commit is contained in:
Steve Howell
2020-02-29 17:41:41 +00:00
committed by Tim Abbott
parent eb368c9c92
commit 49b8218463
2 changed files with 14 additions and 10 deletions

View File

@@ -61,6 +61,7 @@ from zerver.lib.stream_subscription import (
get_bulk_stream_subscriber_info,
get_stream_subscriptions_for_user,
get_stream_subscriptions_for_users,
get_subscribed_stream_ids_for_user,
num_subscribers_for_stream_id,
)
from zerver.lib.stream_topic import StreamTopicTarget
@@ -5436,10 +5437,6 @@ def do_get_streams(
query = get_occupied_streams(user_profile.realm)
if not include_all_active:
user_subs = get_stream_subscriptions_for_user(user_profile).filter(
active=True,
).select_related('recipient')
# We construct a query as the or (|) of the various sources
# this user requested streams from.
query_filter = None # type: Optional[Q]
@@ -5452,17 +5449,17 @@ def do_get_streams(
query_filter |= option
if include_subscribed:
recipient_check = Q(id__in=[sub.recipient.type_id for sub in user_subs])
subscribed_stream_ids = get_subscribed_stream_ids_for_user(user_profile)
recipient_check = Q(id__in=set(subscribed_stream_ids))
add_filter_option(recipient_check)
if include_public:
invite_only_check = Q(invite_only=False)
add_filter_option(invite_only_check)
if include_owner_subscribed and user_profile.is_bot:
assert user_profile.bot_owner is not None
owner_subs = get_stream_subscriptions_for_user(user_profile.bot_owner).filter(
active=True,
).select_related('recipient')
owner_subscribed_check = Q(id__in=[sub.recipient.type_id for sub in owner_subs])
bot_owner = user_profile.bot_owner
assert bot_owner is not None
owner_stream_ids = get_subscribed_stream_ids_for_user(bot_owner)
owner_subscribed_check = Q(id__in=set(owner_stream_ids))
add_filter_option(owner_subscribed_check)
if query_filter is not None:

View File

@@ -24,6 +24,13 @@ def get_active_subscriptions_for_stream_ids(stream_ids: List[int]) -> QuerySet:
active=True
)
def get_subscribed_stream_ids_for_user(user_profile: UserProfile) -> QuerySet:
return Subscription.objects.filter(
user_profile_id=user_profile,
recipient__type=Recipient.STREAM,
active=True,
).values_list('recipient__type_id', flat=True)
def get_stream_subscriptions_for_user(user_profile: UserProfile) -> QuerySet:
# TODO: Change return type to QuerySet[Subscription]
return Subscription.objects.filter(