[IMP] mail, *: optimize query of channel members

* = im_livechat, test_discuss_full

According to stats from our production database, this query is 10 times
faster than the one generated by the ORM by using a `left join` rather
than `id in`.

closes odoo/odoo#125961

Related: odoo/enterprise#43635
Signed-off-by: Didier Debondt (did) <did@odoo.com>
This commit is contained in:
Sébastien Theys
2023-07-05 11:37:16 +02:00
parent 4f6a92e684
commit ef4196281b
4 changed files with 26 additions and 29 deletions
-1
View File
@@ -9,6 +9,5 @@ from . import test_get_discuss_channel
from . import test_get_random_operator
from . import test_im_livechat_report
from . import test_im_livechat_support_page
from . import test_performance
from . import test_js
from . import test_message
@@ -1,20 +0,0 @@
from odoo.addons.test_discuss_full.tests.test_performance import TestDiscussFullPerformance
old_method = TestDiscussFullPerformance._get_init_messaging_result
def _get_init_messaging_result(self):
res = old_method(self)
for channel in res['channels']:
if not channel['channel']['channel_type'] == 'livechat':
continue
for channel_member in channel['channel']['channelMembers'][0][1]:
channel_member['persona']['partner']['is_bot'] = False
return res
def _get_query_count(self):
return 79
TestDiscussFullPerformance._get_init_messaging_result = _get_init_messaging_result
TestDiscussFullPerformance._get_query_count = _get_query_count
+21 -7
View File
@@ -758,13 +758,27 @@ class Channel(models.Model):
rtc_sessions_by_channel = self.sudo().rtc_session_ids._mail_rtc_session_format_by_channel()
channel_last_message_ids = dict((r['id'], r['message_id']) for r in self._channel_last_message_ids())
current_partner, current_guest = self.env["res.partner"]._get_current_persona()
all_needed_members_domain = expression.OR([
[('channel_id.channel_type', '!=', 'channel')],
[('rtc_inviting_session_id', '!=', False)],
[('partner_id', '=', current_partner.id) if current_partner else expression.FALSE_LEAF],
[('guest_id', '=', current_guest.id) if current_guest else expression.FALSE_LEAF],
])
all_needed_members = self.env['discuss.channel.member'].search(expression.AND([[('channel_id', 'in', self.ids)], all_needed_members_domain]), order='id')
self.env['discuss.channel'].flush_model()
self.env['discuss.channel.member'].flush_model()
# Query instead of ORM for performance reasons: "LEFT JOIN" is more
# efficient than "id IN" for the cross-table condition between channel
# (for channel_type) and member (for other fields).
self.env.cr.execute("""
SELECT discuss_channel_member.id
FROM discuss_channel_member
LEFT JOIN discuss_channel
ON discuss_channel.id = discuss_channel_member.channel_id
AND discuss_channel.channel_type != 'channel'
WHERE discuss_channel_member.channel_id in %(channel_ids)s
AND (
discuss_channel.id IS NOT NULL
OR discuss_channel_member.rtc_inviting_session_id IS NOT NULL
OR discuss_channel_member.partner_id = %(current_partner_id)s
OR discuss_channel_member.guest_id = %(current_guest_id)s
)
ORDER BY discuss_channel_member.id ASC
""", {'channel_ids': tuple(self.ids), 'current_partner_id': current_partner.id or None, 'current_guest_id': current_guest.id or None})
all_needed_members = self.env['discuss.channel.member'].browse([m['id'] for m in self.env.cr.dictfetchall()])
all_needed_members.partner_id.sudo().mail_partner_format() # prefetch in batch
members_by_channel = defaultdict(lambda: self.env['discuss.channel.member'])
invited_members_by_channel = defaultdict(lambda: self.env['discuss.channel.member'])
@@ -816,6 +816,7 @@ class TestDiscussFullPerformance(TransactionCase):
'active': True,
'country': [('clear',)],
'id': self.users[0].partner_id.id,
'is_bot': False,
'is_public': False,
'name': 'Ernest Employee',
},
@@ -831,6 +832,7 @@ class TestDiscussFullPerformance(TransactionCase):
'active': True,
'country': [('clear',)],
'id': self.users[1].partner_id.id,
'is_bot': False,
'is_public': False,
'name': 'test1',
},
@@ -897,6 +899,7 @@ class TestDiscussFullPerformance(TransactionCase):
'active': True,
'country': [('clear',)],
'id': self.users[0].partner_id.id,
'is_bot': False,
'is_public': False,
'name': 'Ernest Employee',
},
@@ -911,6 +914,7 @@ class TestDiscussFullPerformance(TransactionCase):
'partner': {
'active': False,
'id': self.env.ref('base.public_partner').id,
'is_bot': False,
'is_public': True,
'name': 'Public user',
},
@@ -1012,4 +1016,4 @@ class TestDiscussFullPerformance(TransactionCase):
Returns the expected query count.
The point of having a separate getter is to allow it to be overriden.
"""
return 75
return 66