[IMP] base_automation: refine CRON frequency

The current 'Base Action Rule: check and execute' CRON search every
four hours for delayed actions to run. This mean, any action scheduled
later can be delayed up to four hours.

We now dynamically reset the CRON frequency according to the least
delayed automated action to minimize the potential gap.

closes odoo/odoo#40633

Task: 1998809
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
Julien Castiaux
2019-11-29 09:07:58 +00:00
committed by Raphael Collet
parent ec9e389f2f
commit dec397b86d
2 changed files with 44 additions and 3 deletions
@@ -25,6 +25,14 @@ DATE_RANGE_FUNCTION = {
False: lambda interval: relativedelta(0),
}
DATE_RANGE_FACTOR = {
'minutes': 1,
'hour': 60,
'day': 24 * 60,
'month': 30 * 24 * 60,
False: 0,
}
class BaseAutomation(models.Model):
_name = 'base.automation'
@@ -64,9 +72,11 @@ class BaseAutomation(models.Model):
trigger_field_ids = fields.Many2many('ir.model.fields', string='Trigger Fields',
help="The action will be triggered if and only if one of these fields is updated."
"If empty, all fields are watched.")
least_delay_msg = fields.Char(compute='_compute_least_delay_msg')
# which fields have an impact on the registry
# which fields have an impact on the registry and the cron
CRITICAL_FIELDS = ['model_id', 'active', 'trigger', 'on_change_field_ids']
RANGE_FIELDS = ['trg_date_range', 'trg_date_range_type']
@api.onchange('model_id')
def onchange_model_id(self):
@@ -109,6 +119,8 @@ class BaseAutomation(models.Model):
if set(vals).intersection(self.CRITICAL_FIELDS):
self._update_cron()
self._update_registry()
elif set(vals).intersection(self.RANGE_FIELDS):
self._update_cron()
return res
def unlink(self):
@@ -119,10 +131,18 @@ class BaseAutomation(models.Model):
def _update_cron(self):
""" Activate the cron job depending on whether there exists action rules
based on time conditions.
based on time conditions. Also update its frequency according to
the smallest action delay, or restore the default 4 hours if there
is no time based action.
"""
cron = self.env.ref('base_automation.ir_cron_data_base_automation_check', raise_if_not_found=False)
return cron and cron.toggle(model=self._name, domain=[('trigger', '=', 'on_time')])
if cron:
actions = self.with_context(active_test=True).search([('trigger', '=', 'on_time')])
cron.try_write({
'active': bool(actions),
'interval_type': 'minutes',
'interval_number': self._get_cron_interval(actions),
})
def _update_registry(self):
""" Update the registry after a modification on action rules. """
@@ -154,6 +174,22 @@ class BaseAutomation(models.Model):
'user': self.env.user,
}
def _get_cron_interval(self, actions=None):
""" Return the expected time interval used by the cron, in minutes. """
def get_delay(rec):
return rec.trg_date_range * DATE_RANGE_FACTOR[rec.trg_date_range_type]
if actions is None:
actions = self.with_context(active_test=True).search([('trigger', '=', 'on_time')])
# Minimum 1 minute, maximum 4 hours, 10% tolerance
delay = min(actions.mapped(get_delay), default=0)
return min(max(1, delay // 10), 4 * 60) if delay else 4 * 60
def _compute_least_delay_msg(self):
msg = _("Note that this action can be trigged up to %d minutes after its schedule.")
self.least_delay_msg = msg % self._get_cron_interval()
def _filter_pre(self, records):
""" Filter the records that satisfy the precondition of action ``self``. """
if self.filter_pre_domain and records:
@@ -66,6 +66,11 @@
<field name="trg_date_calendar_id"
attrs="{'invisible': ['|', ('trg_date_id','=',False), ('trg_date_range_type', '!=', 'day')]}"/>
</xpath>
<xpath expr="//group[@name='action_wrapper']" position="after">
<div class="alert alert-info" role="alert" attrs="{'invisible': [('trigger', '!=', 'on_time')]}">
<field name="least_delay_msg"/>
</div>
</xpath>
</field>
</record>