-
Notifications
You must be signed in to change notification settings - Fork 710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
502 remediation #5963
502 remediation #5963
Conversation
In `send_message` in `utils.py`: * Reduce number of queries * Prefetch group members * One query for users_to_email In `MultiUsernameorGroupnameField` in `form_fields.py` * Get users and groups in one query each * A single pass through to collect `found_names`
e600cc1
to
fc930f3
Compare
fc930f3
to
8d90829
Compare
kitsune/messages/utils.py
Outdated
inbox_messages = [ | ||
InboxMessage(sender=sender, to=user, message=text) for user in all_recipients_of_message | ||
] | ||
messages = InboxMessage.objects.bulk_create(inbox_messages) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the bulk_create()
change! I noticed however that you're not setting the to_group
field on each of the InboxMessage
's like you used to. Are we getting rid of to_group
on the InboxMessage
? If not, I wonder if we could do a bulk_update()
of the to_group
field? That would be nice, but not sure if the bulk_update
works with many-to-many fields.
a2981e5
to
82c249a
Compare
kitsune/messages/utils.py
Outdated
outbox_message.to.set(users) | ||
for obj in to: | ||
if isinstance(obj, User): | ||
usernames.append(obj) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
usernames.append(obj) | |
usernames.append(obj.username) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, since we want the actual User
objects later for setting the to
field of the OutboxMessage
, can't we just collect those here -- in addition to the usernames
above -- by creating another list called users
and doing a users.append(obj)
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is bad naming on behalf of a cut and paste - these are objects being collected, not names. Even my comment is broken here - I'll fix this up. But we get the objects separated here.
kitsune/messages/utils.py
Outdated
if isinstance(obj, User): | ||
usernames.append(obj) | ||
else: | ||
group_names.append(obj) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
group_names.append(obj) | |
group_names.append(obj.name) |
kitsune/messages/utils.py
Outdated
if isinstance(obj, User): | ||
usernames.append(obj) | ||
else: | ||
group_names.append(obj) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we want the actual Group
objects later for setting the to_group
of the OutboxMessage
, can't we just collect those here -- in addition to the group_names
above -- by creating another list called groups
and doing a groups.append(obj)
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - see note above. Bad naming...I'll fix.
kitsune/messages/utils.py
Outdated
else: | ||
group_names.append(obj) | ||
|
||
groups_qs = Group.objects.filter(name__in=group_names) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking we can delete this line, and use the groups
list above instead.
kitsune/messages/utils.py
Outdated
users_qs = User.objects.filter(username__in=usernames).values_list("id", flat=True) | ||
|
||
# Group users' pks only - could be a lot of them | ||
group_users_qs = User.objects.filter(groups__in=groups_qs).values_list("id", flat=True) | ||
|
||
if groups: | ||
# Add the groups from the To field to the message | ||
outbox_message.to_group.set(groups) | ||
# Resolve all unique user ids, including those in groups | ||
# but keep up with users from groups | ||
to_users = set(users_qs) | ||
group_users = set(group_users_qs) | ||
all_recipients_of_message = to_users.union(group_users) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking we could replace all of this with a single query:
all_recipients_of_message = set(User.objects.filter(Q(username__in=usernames) | Q(groups__name__in=group_names)).values_list("id", flat=True))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have to keep up with the users that came from the to
field and the users that came from the groups
from the to field. So capturing the pks in separate sets will matter later. We don't want to set the to
in an outbox message to every user; just to the users that were entered individually.
kitsune/messages/utils.py
Outdated
set_of_user_pks_to_email_private_message = set( | ||
# Create the outbox message | ||
outbox_message = OutboxMessage.objects.create(sender=sender, message=text) | ||
to_users = User.objects.filter(id__in=to_users) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to_users
since we'd already have the users
list from my comments above.
kitsune/messages/utils.py
Outdated
Setting.objects.filter( | ||
user__in=receivers, name="email_private_messages", value=True | ||
).values_list("user__pk", flat=True) | ||
user__in=to_users, name="email_private_messages", value=True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we want include all users, so the users from the groups as well, so I think this should be:
user__in=to_users, name="email_private_messages", value=True | |
user__in=all_recipients_of_message, name="email_private_messages", value=True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are disabling e-mail (not messaging, just email) for users in Groups for now, pending some follow up.
kitsune/messages/utils.py
Outdated
InboxMessage.objects.bulk_create(inbox_messages) | ||
|
||
# Assuming messages are fetched again with IDs if necessary | ||
for user_id in users_to_email: | ||
inbox_messages = InboxMessage.objects.filter(sender=sender, to__id=user_id, message=text) | ||
for message in inbox_messages: | ||
email_private_message.delay(inbox_message_id=message.id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bulk_create
returns the list of created InboxMessage
's -- so they each have a valid id
-- but we're not using that list.
Instead of querying for each InboxMessage
for each of the users_to_email
, which could be a large number of queries, over 2k for the Contributors
group for example, it might be better to use that list of created InboxMessage
's to create a dictionary that maps the to_id
to the id
of each of the messages, and then we can use that mapping to quickly get the message id
for each of the users we need to email.
So I'm thinking of something like:
# Create a mapping from the user id to the message id, so just integer to integer.
recipient_id_to_message_id = {msg.to_id : msg.id for msg in InboxMessage.objects.bulk_create(inbox_messages)}
# Use the mapping to get the message id for each user.
for user_id in users_to_email:
if (message_id := recipient_id_to_message_id.get(user_id)):
email_private_message.delay(inbox_message_id=message_id)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was sketchy in the docs vs. info from other sources. Good to know!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented!
* Drop to_group form inboxmessage model * Use `values_list` for large queries * celery for email
cb970fc
to
de85f24
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r+wc
{%- else -%} | ||
{{ user.profile.display_name }} | ||
{%- endif -%} | ||
{%- if message.recipients_count>1 -%}, ...{% endif %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit. Add spaces around >
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it.
{% set group_slug = profile.slug %} | ||
<a href="{{ url('groups.profile', group_slug) }}">{{ group }}</a> | ||
{%- endfor -%} | ||
{% if message.to_groups_count>1 %}, ...{% endif %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit. Add spaces around >
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it.
{% set group_slug = profile.slug %} | ||
<a href="{{ url('groups.profile', group_slug) }}">{{ group }}</a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit.
{% set group_slug = profile.slug %} | |
<a href="{{ url('groups.profile', group_slug) }}">{{ group }}</a> | |
<a href="{{ url('groups.profile', profile.slug) }}">{{ group }}</a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Setting.objects.filter( | ||
user__in=receivers, name="email_private_messages", value=True | ||
).values_list("user__pk", flat=True) | ||
user__in=users, name="email_private_messages", value=True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wanted to record that I'd prefer this to use all_recipients_of_message
instead of just the users
, but that's for another discussion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it.
Optimize db queries for 502
In
send_message
inutils.py
:In
MultiUsernameorGroupnameField
inform_fields.py
found_names
Refactor
_add_recipients