Skip to content

Commit 7bc3c77

Browse files
committed
Add 'Allow Commit' option on job functions
It is forbidden to commit inside a job, because it releases the job lock and can cause it to start again, while still being run, by the dead jobs requeuer. For some use cases, it may actually be legitimate, or at least be needed in the short term before actual updates in the code. A new option on the job function, false by default, allow to run the job in a new transaction, at the cost of an additional connection + transaction overhead. Related to #889
1 parent 283136f commit 7bc3c77

6 files changed

Lines changed: 36 additions & 9 deletions

File tree

queue_job/controllers/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ def _prevent_commit(cr):
3838
def forbidden_commit(*args, **kwargs):
3939
raise RuntimeError(
4040
"Commit is forbidden in queue jobs. "
41-
"If the current job is a cron running as queue job, "
42-
"modify it to run as a normal cron."
41+
'You may want to enable the "Run in new cursor" option on the Job '
42+
"Function. Alternatively, if the current job is a cron running as "
43+
"queue job, you can modify it to run as a normal cron."
4344
)
4445

4546
original_commit = cr.commit

queue_job/job.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import sys
99
import uuid
1010
import weakref
11+
from contextlib import contextmanager, nullcontext
1112
from datetime import datetime, timedelta
1213
from random import randint
1314

@@ -406,10 +407,6 @@ def __init__(
406407
self.method_name = func.__name__
407408
self.recordset = recordset
408409

409-
self.env = env
410-
self.job_model = self.env["queue.job"]
411-
self.job_model_name = "queue.job"
412-
413410
self.job_config = (
414411
self.env["queue.job.function"].sudo().job_config(self.job_function_name)
415412
)
@@ -487,7 +484,12 @@ def perform(self):
487484
"""
488485
self.retry += 1
489486
try:
490-
self.result = self.func(*tuple(self.args), **self.kwargs)
487+
if self.job_config.allow_commit:
488+
env_context_manager = self._with_temporary_env()
489+
else:
490+
env_context_manager = nullcontext()
491+
with env_context_manager:
492+
self.result = self.func(*tuple(self.args), **self.kwargs)
491493
except RetryableJobError as err:
492494
if err.ignore_retry:
493495
self.retry -= 1
@@ -507,6 +509,14 @@ def perform(self):
507509

508510
return self.result
509511

512+
@contextmanager
513+
def _with_temporary_env(self):
514+
with self.env.registry.cursor() as new_cr:
515+
env = self.recordset.env
516+
self.recordset = self.recordset.with_env(env(cr=new_cr))
517+
yield
518+
self.recordset = self.recordset.with_env(env)
519+
510520
def _get_common_dependent_jobs_query(self):
511521
return """
512522
UPDATE queue_job
@@ -665,6 +675,10 @@ def __hash__(self):
665675
def db_record(self):
666676
return self.db_records_from_uuids(self.env, [self.uuid])
667677

678+
@property
679+
def env(self):
680+
return self.recordset.env
681+
668682
@property
669683
def func(self):
670684
recordset = self.recordset.with_context(job_uuid=self.uuid)

queue_job/models/queue_job_function.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class QueueJobFunction(models.Model):
2828
"related_action_enable "
2929
"related_action_func_name "
3030
"related_action_kwargs "
31-
"job_function_id ",
31+
"job_function_id "
32+
"allow_commit",
3233
)
3334

3435
def _default_channel(self):
@@ -79,6 +80,11 @@ def _default_channel(self):
7980
"enable, func_name, kwargs.\n"
8081
"See the module description for details.",
8182
)
83+
allow_commit = fields.Boolean(
84+
help="Allows the job to commit transactions during execution. "
85+
"Under the hood, this executes the job in a new database cursor, "
86+
"which incurs a slight overhead.",
87+
)
8288

8389
@api.depends("model_id.model", "method")
8490
def _compute_name(self):
@@ -149,6 +155,7 @@ def job_default_config(self):
149155
related_action_func_name=None,
150156
related_action_kwargs={},
151157
job_function_id=None,
158+
allow_commit=False,
152159
)
153160

154161
def _parse_retry_pattern(self):
@@ -184,6 +191,7 @@ def job_config(self, name):
184191
related_action_func_name=config.related_action.get("func_name"),
185192
related_action_kwargs=config.related_action.get("kwargs", {}),
186193
job_function_id=config.id,
194+
allow_commit=config.allow_commit,
187195
)
188196

189197
def _retry_pattern_format_error_message(self):

queue_job/tests/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ def _add_job(self, *args, **kwargs):
276276

277277
def _prepare_context(self, job):
278278
# pylint: disable=context-overridden
279-
job_model = job.job_model.with_context({})
279+
job_model = job.env["queue.job"].with_context({})
280280
field_records = job_model._fields["records"]
281281
# Filter the context to simulate store/load of the job
282282
job.recordset = field_records.convert_to_write(job.recordset, job_model)

queue_job/tests/test_model_job_function.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def test_function_job_config(self):
4242
' "func_name": "related_action_foo",'
4343
' "kwargs": {"b": 1}}'
4444
),
45+
"allow_commit": True,
4546
}
4647
)
4748
self.assertEqual(
@@ -53,5 +54,6 @@ def test_function_job_config(self):
5354
related_action_func_name="related_action_foo",
5455
related_action_kwargs={"b": 1},
5556
job_function_id=job_function.id,
57+
allow_commit=True,
5658
),
5759
)

queue_job/views/queue_job_function_views.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<field name="model_id" required="1" />
1111
<field name="method" required="1" />
1212
<field name="channel_id" />
13+
<field name="allow_commit" />
1314
<field name="edit_retry_pattern" widget="ace" />
1415
<field name="edit_related_action" widget="ace" />
1516
</group>
@@ -24,6 +25,7 @@
2425
<list>
2526
<field name="name" />
2627
<field name="channel_id" />
28+
<field name="allow_commit" />
2729
</list>
2830
</field>
2931
</record>

0 commit comments

Comments
 (0)