I am working on a production module in Odoo (16.x) where a scheduled action (ir.cron) processes records that must be handled exactly once.
This works correctly in a single-worker environment, but breaks under a multi-worker production setup.
from odoo import api, models, fields
import logging
_logger = logging.getLogger(__name__)
class PaymentSync(models.Model):
_name = 'payment.sync'
state = fields.Selection([
('pending', 'Pending'),
('processing', 'Processing'),
('done', 'Done'),
], default='pending')
@api.model
def cron_sync_payments(self):
records = self.search([('state', '=', 'pending')], limit=50)
for rec in records:
try:
rec.state = 'processing'
self.env.cr.commit()
# External API call (takes ~3–5 seconds)
rec._sync_with_gateway()
rec.state = 'done'
self.env.cr.commit()
except Exception as e:
_logger.exception("Payment sync failed: %s", e)
rec.state = 'pending'
self.env.cr.commit()
def _sync_with_gateway(self):
# Simulated external call
pass
This cron runs every minute and multiple Odoo workers are enabled.
Expected Behavior
Each record with state = 'pending' should be:
Picked by only one worker
Processed exactly once
Moved to
donestate after successful execution
Actual Behavior / Error
In production, I observe:
The same record being processed by multiple workers
Duplicate external API calls for the same record
Inconsistent final state (
done→processing→done)No errors raised in logs
Even with:
Explicit state transitions
Manual commits
Record limits
the issue still occurs under load.
What I Have Tried
Adding
limitto the searchForcing commits after state changes
Increasing cron interval
Ensuring only one
ir.cronrecord exists
None of these fully prevent duplicate execution.
Question
How does Odoo handle record selection and execution for cron jobs under a multi-worker setup, and what is the correct locking or transaction-safe pattern to ensure that each record is processed exactly once, even when multiple workers execute the same scheduled action concurrently?