diff --git a/doc/cla/individual/lin-ww.md b/doc/cla/individual/lin-ww.md new file mode 100644 index 00000000000..b84d2eb167f --- /dev/null +++ b/doc/cla/individual/lin-ww.md @@ -0,0 +1,11 @@ +China, 05-28-2022 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Lin Wenwen 514706098@qq.com https://github.com/lin-ww diff --git a/odoo/addons/base/models/ir_cron.py b/odoo/addons/base/models/ir_cron.py index ac48c240b3c..d07e86c7a71 100644 --- a/odoo/addons/base/models/ir_cron.py +++ b/odoo/addons/base/models/ir_cron.py @@ -99,17 +99,22 @@ class ir_cron(models.Model): if not jobs: return cls._check_modules_state(cron_cr, jobs) - job_ids = tuple([job['id'] for job in jobs]) - while True: - job = cls._acquire_one_job(cron_cr, job_ids) + for job_id in (job['id'] for job in jobs): + try: + job = cls._acquire_one_job(cron_cr, (job_id,)) + except psycopg2.extensions.TransactionRollbackError: + cron_cr.rollback() + _logger.debug("job %s has been processed by another worker, skip", job_id) + continue if not job: - break - _logger.debug("job %s acquired", job['id']) + _logger.debug("another worker is processing job %s, skip", job_id) + continue + _logger.debug("job %s acquired", job_id) # take into account overridings of _process_job() on that database registry = odoo.registry(db_name) registry[cls._name]._process_job(db, cron_cr, job) - _logger.debug("job %s updated and released", job['id']) + _logger.debug("job %s updated and released", job_id) except BadVersion: _logger.warning('Skipping database %s as its base version is not %s.', db_name, BASE_VERSION) @@ -191,7 +196,17 @@ class ir_cron(models.Model): @classmethod def _acquire_one_job(cls, cr, job_ids): - """ Acquire one job for update from the job_ids tuple. """ + """ + Acquire for update one job that is ready from the job_ids tuple. + + The jobs that have already been processed in this worker should + be excluded from the tuple. + + This function raises a ``psycopg2.errors.SerializationFailure`` + when the ``nextcall`` of one of the job_ids is modified in + another transaction. You should rollback the transaction and try + again later. + """ # We have to make sure ALL jobs are executed ONLY ONCE no matter # how many cron workers may process them. The exlusion mechanism @@ -204,7 +219,15 @@ class ir_cron(models.Model): # the other workers don't select it too. # (ii) is implemented via the `WHERE` statement, when a job has # been processed, its nextcall is updated to a date in the - # future and the optionnal trigger is removed. + # future and the optional triggers are removed. + # + # Note about (ii): it is possible that a job becomes available + # again quickly (e.g. high frequency or self-triggering cron). + # This function doesn't prevent from acquiring that job multiple + # times at different moments. This can block a worker on + # executing a same job in loop. To prevent this problem, the + # callee is responsible of providing a `job_ids` tuple without + # the jobs it has executed already. # # An `UPDATE` lock type is the strongest row lock, it conflicts # with ALL other lock types. Among them the `KEY SHARE` row lock