mirror of https://github.com/zulip/zulip.git
Create subscription objects in bulk by user_profile.
This, combined with acrefoot's work on sending the notifications in bulk, resolves trac #1142 -- we do only 10 database queries and the whole operation completes in about 300ms on my laptop. (imported from commit 36b5bb836bc6c713903d1ca72e39af87775dc469)
This commit is contained in:
parent
f146362ea9
commit
56949b4788
|
@ -415,10 +415,13 @@ def internal_send_message(sender_email, recipient_type_name, recipients,
|
||||||
if ret is not None:
|
if ret is not None:
|
||||||
logging.error("Error sending internal message by %s: %s" % (sender_email, ret))
|
logging.error("Error sending internal message by %s: %s" % (sender_email, ret))
|
||||||
|
|
||||||
def get_stream_colors(user_profile):
|
|
||||||
return [(sub["name"], sub["color"]) for sub in gather_subscriptions(user_profile)]
|
|
||||||
|
|
||||||
def pick_color(user_profile):
|
def pick_color(user_profile):
|
||||||
|
subs = Subscription.objects.filter(user_profile=user_profile,
|
||||||
|
active=True,
|
||||||
|
recipient__type=Recipient.STREAM)
|
||||||
|
return pick_color_helper(user_profile, subs)
|
||||||
|
|
||||||
|
def pick_color_helper(user_profile, subs):
|
||||||
# These colors are shared with the palette in subs.js.
|
# These colors are shared with the palette in subs.js.
|
||||||
stream_assignment_colors = [
|
stream_assignment_colors = [
|
||||||
"#76ce90", "#fae589", "#a6c7e5", "#e79ab5",
|
"#76ce90", "#fae589", "#a6c7e5", "#e79ab5",
|
||||||
|
@ -427,7 +430,7 @@ def pick_color(user_profile):
|
||||||
"#ee7e4a", "#a6dcbf", "#95a5fd", "#53a063",
|
"#ee7e4a", "#a6dcbf", "#95a5fd", "#53a063",
|
||||||
"#9987e1", "#e4523d", "#c2c2c2", "#4f8de4",
|
"#9987e1", "#e4523d", "#c2c2c2", "#4f8de4",
|
||||||
"#c6a8ad", "#e7cc4d", "#c8bebf", "#a47462"]
|
"#c6a8ad", "#e7cc4d", "#c8bebf", "#a47462"]
|
||||||
used_colors = [elt[1] for elt in get_stream_colors(user_profile) if elt[1]]
|
used_colors = [sub.color for sub in subs if sub.active]
|
||||||
available_colors = filter(lambda x: x not in used_colors,
|
available_colors = filter(lambda x: x not in used_colors,
|
||||||
stream_assignment_colors)
|
stream_assignment_colors)
|
||||||
|
|
||||||
|
@ -450,18 +453,7 @@ def set_stream_color(user_profile, stream_name, color=None):
|
||||||
subscription.save(update_fields=["color"])
|
subscription.save(update_fields=["color"])
|
||||||
return color
|
return color
|
||||||
|
|
||||||
def do_add_subscription(user_profile, stream, no_log=False):
|
def notify_new_subscription(user_profile, stream, subscription, no_log=False):
|
||||||
recipient = get_recipient(Recipient.STREAM, stream.id)
|
|
||||||
color = pick_color(user_profile)
|
|
||||||
(subscription, created) = Subscription.objects.get_or_create(
|
|
||||||
user_profile=user_profile, recipient=recipient,
|
|
||||||
defaults={'active': True, 'color': color})
|
|
||||||
did_subscribe = created
|
|
||||||
if not subscription.active:
|
|
||||||
did_subscribe = True
|
|
||||||
subscription.active = True
|
|
||||||
subscription.save(update_fields=["active"])
|
|
||||||
if did_subscribe:
|
|
||||||
if not no_log:
|
if not no_log:
|
||||||
log_event({'type': 'subscription_added',
|
log_event({'type': 'subscription_added',
|
||||||
'user': user_profile.email,
|
'user': user_profile.email,
|
||||||
|
@ -476,6 +468,60 @@ def do_add_subscription(user_profile, stream, no_log=False):
|
||||||
users=[user_profile.id])
|
users=[user_profile.id])
|
||||||
tornado_callbacks.send_notification(notice)
|
tornado_callbacks.send_notification(notice)
|
||||||
|
|
||||||
|
# This function assumes that the users are known to not be subscribers
|
||||||
|
# of the stream (e.g. because the stream was created by this same query)
|
||||||
|
def bulk_add_subscriptions(stream, users):
|
||||||
|
recipient = get_recipient(Recipient.STREAM, stream.id)
|
||||||
|
all_subs = Subscription.objects.filter(user_profile__in=users,
|
||||||
|
recipient__type=Recipient.STREAM)
|
||||||
|
|
||||||
|
subs_by_user = defaultdict(list)
|
||||||
|
for sub in all_subs:
|
||||||
|
subs_by_user[sub.user_profile_id].append(sub)
|
||||||
|
|
||||||
|
already_subscribed = []
|
||||||
|
subs_to_activate = []
|
||||||
|
users_needing_new_subs = []
|
||||||
|
for user_profile in users:
|
||||||
|
needs_new_sub = True
|
||||||
|
for sub in subs_by_user[user_profile.id]:
|
||||||
|
if sub.recipient_id == recipient.id:
|
||||||
|
needs_new_sub = False
|
||||||
|
if sub.active:
|
||||||
|
already_subscribed.append(user_profile)
|
||||||
|
else:
|
||||||
|
subs_to_activate.append(sub)
|
||||||
|
if needs_new_sub:
|
||||||
|
users_needing_new_subs.append(user_profile)
|
||||||
|
|
||||||
|
subs_to_add = []
|
||||||
|
for user_profile in users_needing_new_subs:
|
||||||
|
color = pick_color_helper(user_profile, subs_by_user[user_profile.id])
|
||||||
|
subs_to_add.append(Subscription(user_profile=user_profile,
|
||||||
|
active=True, color=color,
|
||||||
|
recipient=recipient))
|
||||||
|
Subscription.objects.bulk_create(subs_to_add)
|
||||||
|
Subscription.objects.filter(id__in=[s.id for s in subs_to_activate]).update(active=True)
|
||||||
|
|
||||||
|
for sub in subs_to_add + subs_to_activate:
|
||||||
|
notify_new_subscription(sub.user_profile, stream, sub)
|
||||||
|
return (users_needing_new_subs + [sub.user_profile for sub in subs_to_activate],
|
||||||
|
already_subscribed)
|
||||||
|
|
||||||
|
# When changing this, also change bulk_add_subscriptions
|
||||||
|
def do_add_subscription(user_profile, stream, no_log=False):
|
||||||
|
recipient = get_recipient(Recipient.STREAM, stream.id)
|
||||||
|
color = pick_color(user_profile)
|
||||||
|
(subscription, created) = Subscription.objects.get_or_create(
|
||||||
|
user_profile=user_profile, recipient=recipient,
|
||||||
|
defaults={'active': True, 'color': color})
|
||||||
|
did_subscribe = created
|
||||||
|
if not subscription.active:
|
||||||
|
did_subscribe = True
|
||||||
|
subscription.active = True
|
||||||
|
subscription.save(update_fields=["active"])
|
||||||
|
if did_subscribe:
|
||||||
|
notify_new_subscription(user_profile, stream, subscription, no_log)
|
||||||
return did_subscribe
|
return did_subscribe
|
||||||
|
|
||||||
def do_remove_subscription(user_profile, stream, no_log=False):
|
def do_remove_subscription(user_profile, stream, no_log=False):
|
||||||
|
|
|
@ -27,7 +27,7 @@ from zephyr.lib.actions import do_add_subscription, do_remove_subscription, \
|
||||||
do_send_confirmation_email, do_activate_user, do_create_user, check_send_message, \
|
do_send_confirmation_email, do_activate_user, do_create_user, check_send_message, \
|
||||||
log_subscription_property_change, internal_send_message, \
|
log_subscription_property_change, internal_send_message, \
|
||||||
create_stream_if_needed, gather_subscriptions, subscribed_to_stream, \
|
create_stream_if_needed, gather_subscriptions, subscribed_to_stream, \
|
||||||
update_user_presence, set_stream_color, get_stream_colors, update_message_flags, \
|
update_user_presence, bulk_add_subscriptions, update_message_flags, \
|
||||||
recipient_for_emails, extract_recipients, do_events_register, do_finish_tutorial, \
|
recipient_for_emails, extract_recipients, do_events_register, do_finish_tutorial, \
|
||||||
get_status_dict, do_change_enable_offline_email_notifications, \
|
get_status_dict, do_change_enable_offline_email_notifications, \
|
||||||
do_update_onboarding_steps
|
do_update_onboarding_steps
|
||||||
|
@ -1175,11 +1175,10 @@ def add_subscriptions_backend(request, user_profile,
|
||||||
|
|
||||||
result = dict(subscribed=defaultdict(list), already_subscribed=defaultdict(list))
|
result = dict(subscribed=defaultdict(list), already_subscribed=defaultdict(list))
|
||||||
for stream in streams:
|
for stream in streams:
|
||||||
for subscriber in subscribers:
|
(subscribed, already_subscribed) = bulk_add_subscriptions(stream, subscribers)
|
||||||
did_subscribe = do_add_subscription(subscriber, stream)
|
for subscriber in subscribed:
|
||||||
if did_subscribe:
|
|
||||||
result["subscribed"][subscriber.email].append(stream.name)
|
result["subscribed"][subscriber.email].append(stream.name)
|
||||||
else:
|
for subscriber in already_subscribed:
|
||||||
result["already_subscribed"][subscriber.email].append(stream.name)
|
result["already_subscribed"][subscriber.email].append(stream.name)
|
||||||
private_streams[stream.name] = stream.invite_only
|
private_streams[stream.name] = stream.invite_only
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue