From 40b192dbbcae0a566abc5ab0147292bbaca88c42 Mon Sep 17 00:00:00 2001 From: "Loan (LSE)" Date: Thu, 9 Feb 2023 17:19:30 +0000 Subject: [PATCH] [FIX] hw_drivers: Avoid duplicated actions execution Before this commit: Assuming we have a "Shipping Labels Printer" set on an operation type. Assuming we also have a delivery carrier which send Label through the chatter of this picking type (like DHL or BPost). If we have several sessions connected with the same user (author of the chatter message), for examples, on different devices. Each session will send the same IoT request to print the label (as the code rely on the bus). In consequences, the same label will be printed multiple times After this commit: The label is print only once Associated enterprise PR: https://github.com/odoo/enterprise/pull/36904 opw-3081423 closes odoo/odoo#117251 X-original-commit: 8ca27ae07b0a7b3bf27ac5459d74f28bbcc89ce0 Related: odoo/enterprise#39087 Signed-off-by: Sens Loan (lse) --- addons/hw_drivers/controllers/driver.py | 9 +++++++++ addons/hw_drivers/driver.py | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/addons/hw_drivers/controllers/driver.py b/addons/hw_drivers/controllers/driver.py index b82800e55a8..3f1c526cb66 100755 --- a/addons/hw_drivers/controllers/driver.py +++ b/addons/hw_drivers/controllers/driver.py @@ -30,6 +30,15 @@ class DriverController(http.Controller): if iot_device: iot_device.data['owner'] = session_id data = json.loads(data) + + # Skip the request if it was already executed (duplicated action calls) + iot_idempotent_id = data.get("iot_idempotent_id") + if iot_idempotent_id: + idempotent_session = iot_device._check_idempotency(iot_idempotent_id, session_id) + if idempotent_session: + _logger.info("Ignored request from %s as iot_idempotent_id %s already received from session %s", + session_id, iot_idempotent_id, idempotent_session) + return False iot_device.action(data) return True return False diff --git a/addons/hw_drivers/driver.py b/addons/hw_drivers/driver.py index 0eaf5e1c226..812c7e5a733 100644 --- a/addons/hw_drivers/driver.py +++ b/addons/hw_drivers/driver.py @@ -4,6 +4,7 @@ from threading import Thread, Event from odoo.addons.hw_drivers.main import drivers, iot_devices +from odoo.tools.lru import LRU class DriverMetaClass(type): @@ -35,6 +36,9 @@ class Driver(Thread, metaclass=DriverMetaClass): self._actions = {} self._stopped = Event() + # Least Recently Used (LRU) Cache that will store the idempotent keys already seen. + self._iot_idempotent_ids_cache = LRU(500) + @classmethod def supported(cls, device): """ @@ -54,3 +58,18 @@ class Driver(Thread, metaclass=DriverMetaClass): def disconnect(self): self._stopped.set() del iot_devices[self.device_identifier] + + def _check_idempotency(self, iot_idempotent_id, session_id): + """ + Some IoT requests for the same action might be received several times. + To avoid duplicating the resulting actions, we check if the action was "recently" executed. + If this is the case, we will simply ignore the action + + :return: the `session_id` of the same `iot_idempotent_id` if any. False otherwise, + which means that it is the first time that the IoT box received the request with this ID + """ + cache = self._iot_idempotent_ids_cache + if iot_idempotent_id in cache: + return cache[iot_idempotent_id] + cache[iot_idempotent_id] = session_id + return False