Source code for connector.components.locker

# Copyright 2018 Camptocamp SA
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)

import logging
import psycopg2

from odoo.addons.component.core import Component
from odoo.addons.connector.exception import RetryableJobError  # pylint: disable=W7950

_logger = logging.getLogger(__name__)


[docs]class RecordLocker(Component): """Component allowing to lock record(s) for the current transaction Example of usage:: self.component('record.locker').lock(self.records) See the definition of :meth:`~lock` for details. """ _name = "base.record.locker" _inherit = ["base.connector"] _usage = "record.locker"
[docs] def lock(self, records, seconds=None, ignore_retry=True): """Lock the records. Lock the record so we are sure that only one job is running for this record(s) if concurrent jobs have to run a job for the same record(s). When concurrent jobs try to work on the same record(s), the first one will lock and proceed, the others will fail to acquire it and will be retried later (:exc:`~odoo.addons.queue_job.exception.RetryableJobError` is raised). The lock is using a ``FOR UPDATE NOWAIT`` so any concurrent transaction trying FOR UPDATE/UPDATE will be rejected until the current transaction is committed or rollbacked. A classical use case for this is to prevent concurrent exports. The following parameters are forwarded to the exception :exc:`~odoo.addons.queue_job.exception.RetryableJobError` :param seconds: In case of retry because the lock cannot be acquired, in how many seconds it must be retried. If not set, the queue_job configuration is used. :param ignore_retry: If True, the retry counter of the job will not be increased. """ sql = ("SELECT id FROM %s WHERE ID IN %%s FOR UPDATE NOWAIT" % self.model._table) try: self.env.cr.execute(sql, (tuple(records.ids),), log_exceptions=False) except psycopg2.OperationalError: _logger.info( "A concurrent job is already working on the same " "record (%s with one id in %s). Job delayed later.", self.model._name, tuple(records.ids), ) raise RetryableJobError( "A concurrent job is already working on the same record " "(%s with one id in %s). The job will be retried later." % (self.model._name, tuple(records.ids),), seconds=seconds, ignore_retry=ignore_retry )