Skip to content

delayable attribute lost when the @job decorator is not added on base method #273

@guewen

Description

@guewen

Example of error message:

AttributeError: method cron_actions on stock.buffer(5577,) is not allowed to be delayed, it should be decorated with odoo.addons.queue_job.job.job

If I have such dependencies:

module_a
  |- module_b
  |- module_c

module_b and module_c have no dependencies on each other.

In module_a, we have a method StockMove._update_foo.
In module_b, we add @job on StockMove._update_foo.
In module_c, we have another override of StockMove._update_foo, without decorator.

It seems that depending on the loading order, we'll lose the properties added by @job.

AFAIU, it happens because of the way the registry builds a new instance based on a recomposed dependency graph, we can't be sure the kept function will be the one which has been decorated and has the delayable attribute. We would need to propagate it.

That being said, the delayable attribute is there only to check that we decorated the method with @job. If we remove the check at

queue/queue_job/job.py

Lines 81 to 85 in d465bdd

if not getattr(recordset_method, "delayable", None):
raise AttributeError(
"method %s on %s is not allowed to be delayed, "
"it should be decorated with odoo.addons.queue_job.job.job"
% (name, self.recordset)
, the enqueuing works perfectly.

We can consider removing the need for @job (make it optional with default values). That would imply:

  • stop storing the @job attributes on the method (

    queue/queue_job/job.py

    Lines 828 to 831 in d465bdd

    func.delayable = True
    func.delay = delay_func
    func.retry_pattern = retry_pattern
    func.default_channel = default_channel
    ): delayable could be removed, delay is deprecated, remains retry_pattern and default_channel to store elsewhere, best place would be in queue.job.function I think as we could change them from the UI
  • same for the related actions attrs...

    queue/queue_job/job.py

    Lines 897 to 898 in d465bdd

    func.related_action = action
    func.kwargs = kwargs
  • think about the security implications by not having the @job allow list
  • delayable is used to find the job methods used to create queue.job.function (
    def _register_hook(self):
    """Register marked jobs"""
    super(Base, self)._register_hook()
    job_methods = [
    method
    for __, method in inspect.getmembers(
    self.__class__, predicate=inspect.isfunction
    )
    if getattr(method, "delayable", None)
    ]
    for job_method in job_methods:
    self.env["queue.job.function"]._register_job(self, job_method)
    ), have to think if we can do it another way (create queue.job.function the first time it's delayed? but then we don't have the ability to put default options...)

Originally posted by @guewen in #270 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions