From 32a82def4f385da1c6f43c43b185dea90130db45 Mon Sep 17 00:00:00 2001 From: Ivan Yelizariev Date: Tue, 9 Feb 2021 15:50:17 +0000 Subject: [PATCH] [FIX] bus: ignore concurrency errors on presence update /longpolling/poll requests are most often requests to Odoo. Moreover, such requests may be sent at the same moment from all users. For example, when all users are subscribed to a common channel and someone sent a message, all current polls are closed to deliver the notification and after that, all clients start the request again. If some users have several clients open (e.g. on desktop and mobile), they may send many parallel requests and hence make concurrent queries to update presence. We don't need be sure that every such query is processed. So, just fail fast and carry on polling. To test perfomance impact of this commit, copy curl command for poll request from browser network tool and repeatly execute it, e.g., ``` for i in {1..1000} do sleep 0.1 curl ... & done ``` Without this commit you may notice such warnings in the logs: ``` ... odoo.service.model: SERIALIZATION_FAILURE, retry 1/5 in 0.2071 sec... ``` At that moment, try to make normal odoo operations (e.g. create a sale order): it would work slower than usual. --- opw-2451865 close #57067 closes odoo/odoo#67390 closes odoo/odoo#67440 X-original-commit: aad9cbe80dae28c0869fe6f543fbe0118357ce57 Signed-off-by: Ivan Yelizariev // IEL Signed-off-by: Raphael Collet (rco) --- addons/bus/models/bus_presence.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/addons/bus/models/bus_presence.py b/addons/bus/models/bus_presence.py index c1da707b215..d25ef09314a 100644 --- a/addons/bus/models/bus_presence.py +++ b/addons/bus/models/bus_presence.py @@ -2,9 +2,12 @@ import datetime import time +from psycopg2 import OperationalError + from odoo import api, fields, models from odoo import tools from odoo.addons.bus.models.bus import TIMEOUT +from odoo.service.model import PG_CONCURRENCY_ERRORS_TO_RETRY from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT DISCONNECTION_TIMER = TIMEOUT + 5 @@ -34,6 +37,23 @@ class BusPresence(models.Model): """ Updates the last_poll and last_presence of the current user :param inactivity_period: duration in milliseconds """ + # This method is called in method _poll() and cursor is closed right + # after; see bus/controllers/main.py. + try: + # Hide transaction serialization errors, which can be ignored, the presence update is not essential + # The errors are supposed from presence.write(...) call only + with tools.mute_logger('odoo.sql_db'): + self._update(inactivity_period) + # commit on success + self.env.cr.commit() + except OperationalError as e: + if e.pgcode in PG_CONCURRENCY_ERRORS_TO_RETRY: + # ignore concurrency error + return self.env.cr.rollback() + raise + + @api.model + def _update(self, inactivity_period): presence = self.search([('user_id', '=', self._uid)], limit=1) # compute last_presence timestamp last_presence = datetime.datetime.now() - datetime.timedelta(milliseconds=inactivity_period) @@ -48,8 +68,4 @@ class BusPresence(models.Model): else: # update the last_presence if necessary, and write values if presence.last_presence < last_presence: values['last_presence'] = last_presence - # Hide transaction serialization errors, which can be ignored, the presence update is not essential - with tools.mute_logger('odoo.sql_db'): - presence.write(values) - # avoid TransactionRollbackError - self.env.cr.commit() # TODO : check if still necessary + presence.write(values)