[FIX] mail, sms, snailmail: fix perf of fetching failures of user

We are currently lacking an index with (author, failure) due to the information
being spread out in two different tables which are both rather big (there are a
lot of non-failure messages for the current user, as well as a lot of failure
messages for other users), so the fetch of failures for the current user can
become extremely slow (> 20s).

With this new index, the query takes less than 1ms.

task-2742946

closes odoo/odoo#83451

Related: odoo/upgrade#3204
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
Sébastien Theys
2022-02-01 18:07:02 +00:00
parent 79f7c233ec
commit 47bda95b98
9 changed files with 49 additions and 23 deletions
+5 -1
View File
@@ -16,6 +16,7 @@ class MailNotification(models.Model):
_description = 'Message Notifications'
# origin
author_id = fields.Many2one('res.partner', 'Author', ondelete='set null')
mail_message_id = fields.Many2one('mail.message', 'Message', index=True, ondelete='cascade', required=True)
mail_mail_id = fields.Many2one('mail.mail', 'Mail', index=True, help='Optional mail_mail ID. Used mainly to optimize searches.')
# recipient
@@ -57,7 +58,10 @@ class MailNotification(models.Model):
def init(self):
self._cr.execute("""
CREATE INDEX IF NOT EXISTS mail_notification_res_partner_id_is_read_notification_status_mail_message_id
ON mail_notification (res_partner_id, is_read, notification_status, mail_message_id)
ON mail_notification (res_partner_id, is_read, notification_status, mail_message_id);
CREATE INDEX IF NOT EXISTS mail_notification_author_id_notification_status_failure
ON mail_notification (author_id, notification_status)
WHERE notification_status IN ('bounce', 'exception');
""")
self.env.cr.execute(
"""CREATE UNIQUE INDEX IF NOT EXISTS unique_mail_message_id_res_partner_id_if_set
+14 -6
View File
@@ -219,12 +219,18 @@ class MailThread(models.AbstractModel):
def _compute_message_has_error(self):
res = {}
if self.ids:
self._cr.execute(""" SELECT msg.res_id, COUNT(msg.res_id) FROM mail_message msg
RIGHT JOIN mail_notification rel
ON rel.mail_message_id = msg.id AND rel.notification_status in ('exception','bounce')
WHERE msg.author_id = %s AND msg.model = %s AND msg.res_id in %s AND msg.message_type != 'user_notification'
GROUP BY msg.res_id""",
(self.env.user.partner_id.id, self._name, tuple(self.ids),))
self.env.cr.execute("""
SELECT msg.res_id, COUNT(msg.res_id)
FROM mail_message msg
INNER JOIN mail_notification notif
ON notif.mail_message_id = msg.id
WHERE notif.notification_status in ('exception', 'bounce')
AND notif.author_id = %(author_id)s
AND msg.model = %(model_name)s
AND msg.res_id in %(res_ids)s
AND msg.message_type != 'user_notification'
GROUP BY msg.res_id
""", {'author_id': self.env.user.partner_id.id, 'model_name': self._name, 'res_ids': tuple(self.ids)})
res.update(self._cr.fetchall())
for record in self:
@@ -2187,6 +2193,7 @@ class MailThread(models.AbstractModel):
inbox_pids = [r['id'] for r in recipients_data if r['notif'] == 'inbox']
if inbox_pids:
notif_create_values = [{
'author_id': message.author_id.id,
'mail_message_id': message.id,
'res_partner_id': pid,
'notification_type': 'inbox',
@@ -2313,6 +2320,7 @@ class MailThread(models.AbstractModel):
'mail_mail_id': new_email.id,
})
notif_create_values += [{
'author_id': message.author_id.id,
'mail_message_id': message.id,
'res_partner_id': recipient_id,
'notification_type': 'email',
+6 -6
View File
@@ -126,14 +126,14 @@ class Partner(models.Model):
"""Returns all messages, sent by the current partner, that have errors, in
the format expected by the web client."""
self.ensure_one()
messages = self.env['mail.message'].search([
('has_error', '=', True),
notifications = self.env['mail.notification'].search([
('author_id', '=', self.id),
('res_id', '!=', 0),
('model', '!=', False),
('message_type', '!=', 'user_notification')
('notification_status', 'in', ('bounce', 'exception')),
('mail_message_id.message_type', '!=', 'user_notification'),
('mail_message_id.model', '!=', False),
('mail_message_id.res_id', '!=', 0),
])
return messages._message_notification_format()
return notifications.mail_message_id._message_notification_format()
def _get_channels_as_member(self):
"""Returns the channels of the partner."""
+1
View File
@@ -737,6 +737,7 @@ class MailCase(MockEmail):
n.is_read == nis_read
)
self.assertTrue(partner_notif, 'Mail: not found notification for %s (type: %s, state: %s, message: %s)' % (partner, ntype, nstatus, message.id))
self.assertEqual(partner_notif.author_id, partner_notif.mail_message_id.author_id, 'Mail: Message and notification should have the same author')
# prepare further asserts
if ntype == 'email':
+5 -4
View File
@@ -24,10 +24,11 @@ class MailResendCancel(models.TransientModel):
FROM mail_notification notif
JOIN mail_message mes
ON notif.mail_message_id = mes.id
WHERE notif.notification_type = 'email' AND notif.notification_status IN ('bounce', 'exception')
AND mes.model = %s
AND mes.author_id = %s
""", (wizard.model, author_id))
WHERE notif.notification_type = 'email'
AND notif.author_id = %(author_id)s
AND notif.notification_status IN ('bounce', 'exception')
AND mes.model = %(model_name)s
""", {'model_name': wizard.model, 'author_id': author_id})
res = self._cr.fetchall()
notif_ids = [row[0] for row in res]
messages_ids = list(set([row[1] for row in res]))
+14 -6
View File
@@ -20,12 +20,19 @@ class MailThread(models.AbstractModel):
def _compute_message_has_sms_error(self):
res = {}
if self.ids:
self._cr.execute(""" SELECT msg.res_id, COUNT(msg.res_id) FROM mail_message msg
RIGHT JOIN mail_notification rel
ON rel.mail_message_id = msg.id AND rel.notification_type = 'sms' AND rel.notification_status in ('exception')
WHERE msg.author_id = %s AND msg.model = %s AND msg.res_id in %s AND msg.message_type != 'user_notification'
GROUP BY msg.res_id""",
(self.env.user.partner_id.id, self._name, tuple(self.ids),))
self.env.cr.execute("""
SELECT msg.res_id, COUNT(msg.res_id)
FROM mail_message msg
INNER JOIN mail_notification notif
ON notif.mail_message_id = msg.id
WHERE notif.notification_type = 'sms'
AND notif.notification_status = 'exception'
AND notif.author_id = %(author_id)s
AND msg.model = %(model_name)s
AND msg.res_id in %(res_ids)s
AND msg.message_type != 'user_notification'
GROUP BY msg.res_id
""", {'author_id': self.env.user.partner_id.id, 'model_name': self._name, 'res_ids': tuple(self.ids)})
res.update(self._cr.fetchall())
for record in self:
@@ -331,6 +338,7 @@ class MailThread(models.AbstractModel):
existing_numbers.append(n.sms_number)
notif_create_values = [{
'author_id': message.author_id.id,
'mail_message_id': message.id,
'res_partner_id': sms.partner_id.id,
'sms_number': sms.number,
+1
View File
@@ -218,6 +218,7 @@ class SMSCase(MockSMS):
notif = notifications.filtered(lambda n: n.res_partner_id == partner and n.sms_number == number and n.notification_status == state)
self.assertTrue(notif, 'SMS: not found notification for %s (number: %s, state: %s)' % (partner, number, state))
self.assertEqual(notif.author_id, notif.mail_message_id.author_id, 'SMS: Message and notification should have the same author')
if state not in ('sent', 'ready', 'canceled'):
self.assertEqual(notif.failure_type, recipient_info['failure_type'])
@@ -100,6 +100,7 @@ class SnailmailLetter(models.Model):
notification_vals = []
for letter in letters:
notification_vals.append({
'author_id': letter.message_id.author_id.id,
'mail_message_id': letter.message_id.id,
'res_partner_id': letter.partner_id.id,
'notification_type': 'snail',
@@ -26,6 +26,7 @@ class TestSMSActionsCommon(TestMailFullCommon, TestMailFullRecipients):
'state': 'error',
})
cls.notif_p1 = cls.env['mail.notification'].create({
'author_id': cls.msg.author_id.id,
'mail_message_id': cls.msg.id,
'res_partner_id': cls.partner_1.id,
'sms_id': cls.sms_p1.id,
@@ -43,6 +44,7 @@ class TestSMSActionsCommon(TestMailFullCommon, TestMailFullRecipients):
'state': 'error',
})
cls.notif_p2 = cls.env['mail.notification'].create({
'author_id': cls.msg.author_id.id,
'mail_message_id': cls.msg.id,
'res_partner_id': cls.partner_2.id,
'sms_id': cls.sms_p2.id,