Skip to content

Commit

Permalink
Add method to patch a method to be automatically delayed
Browse files Browse the repository at this point in the history
This patch method has to be called in ``_register_hook``.

When a method is patched, any call to the method will not directly
execute the method's body, but will instead enqueue a job.

When a ``context_key`` is set when calling ``_patch_job_auto_delay``,
the patched method is automatically delayed only when this key is
``True`` in the caller's context. It is advised to patch the method
with a ``context_key``, because making the automatic delay *in any
case* can produce nasty and unexpected side effects (e.g. another
module calls the method and expects it to be computed before doing
something else, expecting a result, ...).

A typical use case is when a method in a module we don't control is called
synchronously in the middle of another method, and we'd like all the calls
to this method become asynchronous.

It relies on OCA#274 that deprecates the
`@job` decorator.
  • Loading branch information
guewen authored and nguyenminhchien committed Nov 29, 2023
1 parent 6a49aa8 commit 9fc3908
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
27 changes: 27 additions & 0 deletions test_queue_job/models/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,33 @@ def job_alter_mutable(self, mutable_arg, mutable_kwarg=None):
mutable_kwarg["b"] = 2
return mutable_arg, mutable_kwarg

def delay_me(self, arg, kwarg=None):
return arg, kwarg

def delay_me_options_job_options(self):
return {
"identity_key": "my_job_identity",
}

def delay_me_options(self):
return "ok"

def delay_me_context_key(self):
return "ok"

def _register_hook(self):
self._patch_method("delay_me", self._patch_job_auto_delay("delay_me"))
self._patch_method(
"delay_me_options", self._patch_job_auto_delay("delay_me_options")
)
self._patch_method(
"delay_me_context_key",
self._patch_job_auto_delay(
"delay_me_context_key", context_key="auto_delay_delay_me_context_key"
),
)
return super()._register_hook()


class TestQueueChannel(models.Model):

Expand Down
1 change: 1 addition & 0 deletions test_queue_job/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from . import test_autovacuum
from . import test_job
from . import test_job_auto_delay
from . import test_job_channels
from . import test_related_actions
54 changes: 54 additions & 0 deletions test_queue_job/tests/test_job_auto_delay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright 2020 Camptocamp SA
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)

from odoo.tests.common import tagged

from odoo.addons.queue_job.job import Job

from .common import JobCommonCase


@tagged("post_install", "-at_install")
class TestJobAutoDelay(JobCommonCase):
"""Test auto delay of jobs"""

def test_auto_delay(self):
"""method decorated by @job_auto_delay is automatically delayed"""
result = self.env["test.queue.job"].delay_me(1, kwarg=2)
self.assertTrue(isinstance(result, Job))
self.assertEqual(result.args, (1,))
self.assertEqual(result.kwargs, {"kwarg": 2})

def test_auto_delay_options(self):
"""method automatically delayed une <method>_job_options arguments"""
result = self.env["test.queue.job"].delay_me_options()
self.assertTrue(isinstance(result, Job))
self.assertEqual(result.identity_key, "my_job_identity")

def test_auto_delay_inside_job(self):
"""when a delayed job is processed, it must not delay itself"""
job_ = self.env["test.queue.job"].delay_me(1, kwarg=2)
self.assertTrue(job_.perform(), (1, 2))

def test_auto_delay_force_sync(self):
"""method forced to run synchronously"""
result = (
self.env["test.queue.job"]
.with_context(_job_force_sync=True)
.delay_me(1, kwarg=2)
)
self.assertTrue(result, (1, 2))

def test_auto_delay_context_key_set(self):
"""patched with context_key delays only if context keys is set"""
result = (
self.env["test.queue.job"]
.with_context(auto_delay_delay_me_context_key=True)
.delay_me_context_key()
)
self.assertTrue(isinstance(result, Job))

def test_auto_delay_context_key_unset(self):
"""patched with context_key do not delay if context keys is not set"""
result = self.env["test.queue.job"].delay_me_context_key()
self.assertEqual(result, "ok")

0 comments on commit 9fc3908

Please sign in to comment.