From 461f1c7998bc8deb995aa2c42496799055475392 Mon Sep 17 00:00:00 2001 From: tsm-odoo Date: Tue, 11 Oct 2022 14:59:39 +0000 Subject: [PATCH] [IMP] bus: last notification id reset after restoring db MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this PR, the last notification id known by the client was not reset after restoring the database. This was an issue since there can be a gap between the last notification and the one that has been restored with the database. In this scenario, messages are not received after restoring the database since the client subscribes to higher notification ids that the ones that are created. This PR fixes this issue by defaulting to 0 if the one the client passed is higher. closes odoo/odoo#103311 X-original-commit: 5118da53a6e9adb94577a4f16999522a7e1e8c10 Signed-off-by: Sébastien Theys (seb) Signed-off-by: Stockbauer Matthieu (tsm) --- addons/bus/controllers/websocket.py | 3 ++ addons/bus/models/ir_websocket.py | 3 ++ addons/bus/tests/test_websocket_caryall.py | 36 ++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/addons/bus/controllers/websocket.py b/addons/bus/controllers/websocket.py index 4f1aea1edf1..c3840370a86 100644 --- a/addons/bus/controllers/websocket.py +++ b/addons/bus/controllers/websocket.py @@ -44,6 +44,9 @@ class WebsocketController(Controller): channel_with_db(request.db, c) for c in request.env['ir.websocket']._build_bus_channel_list(channels) )) + last_known_notification_id = request.env['bus.bus'].sudo().search([], limit=1, order='id desc').id or 0 + if last > last_known_notification_id: + last = 0 notifications = request.env['bus.bus']._poll(channels, last) return {'channels': channels, 'notifications': notifications} diff --git a/addons/bus/models/ir_websocket.py b/addons/bus/models/ir_websocket.py index 5c1b6b3ffd0..68d187ad974 100644 --- a/addons/bus/models/ir_websocket.py +++ b/addons/bus/models/ir_websocket.py @@ -32,6 +32,9 @@ class IrWebsocket(models.AbstractModel): def _subscribe(self, data): if not all(isinstance(c, str) for c in data['channels']): raise ValueError("bus.Bus only string channels are allowed.") + last_known_notification_id = self.env['bus.bus'].sudo().search([], limit=1, order='id desc').id or 0 + if data['last'] > last_known_notification_id: + data['last'] = 0 channels = set(self._build_bus_channel_list(data['channels'])) dispatch.subscribe(channels, data['last'], self.env.registry.db_name, wsrequest.ws) diff --git a/addons/bus/tests/test_websocket_caryall.py b/addons/bus/tests/test_websocket_caryall.py index fcb02ebf076..07bdc5740a2 100644 --- a/addons/bus/tests/test_websocket_caryall.py +++ b/addons/bus/tests/test_websocket_caryall.py @@ -248,3 +248,39 @@ class TestWebsocketCaryall(WebsocketCase): # But ChromeHeadless still can. headers = ['User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/102.0.0.0 Safari/537.36'] self.websocket_connect() + + def test_subscribe_higher_last_notification_id(self): + subscribe_done_event = Event() + server_last_notification_id = self.env['bus.bus'].sudo().search([], limit=1, order='id desc').id or 0 + client_last_notification_id = server_last_notification_id + 1 + + def subscribe_side_effect(_, last): + # Last notification id given by the client is higher than + # the one known by the server, should default to 0. + self.assertEqual(last, 0) + subscribe_done_event.set() + + with patch.object(Websocket, 'subscribe', side_effect=subscribe_side_effect): + websocket = self.websocket_connect() + websocket.send(json.dumps({ + 'event_name': 'subscribe', + 'data': {'channels': ['my_channel'], 'last': client_last_notification_id} + })) + subscribe_done_event.wait() + + def test_subscribe_lower_last_notification_id(self): + subscribe_done_event = Event() + server_last_notification_id = self.env['bus.bus'].sudo().search([], limit=1, order='id desc').id or 0 + client_last_notification_id = server_last_notification_id - 1 + + def subscribe_side_effect(_, last): + self.assertEqual(last, client_last_notification_id) + subscribe_done_event.set() + + with patch.object(Websocket, 'subscribe', side_effect=subscribe_side_effect): + websocket = self.websocket_connect() + websocket.send(json.dumps({ + 'event_name': 'subscribe', + 'data': {'channels': ['my_channel'], 'last': client_last_notification_id} + })) + subscribe_done_event.wait()