diff --git a/README.md b/README.md
index c4c309ed68..ec5dc193cf 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ Available addons
addon | version | maintainers | summary
--- | --- | --- | ---
[base_import_async](base_import_async/) | 18.0.1.0.0 | | Import CSV files in the background
-[queue_job](queue_job/) | 18.0.2.0.10 |
| Job Queue
+[queue_job](queue_job/) | 18.0.2.1.0 |
| Job Queue
[queue_job_batch](queue_job_batch/) | 18.0.1.0.0 | | Job Queue Batch
[queue_job_cron](queue_job_cron/) | 18.0.1.1.1 | | Scheduled Actions as Queue Jobs
[queue_job_cron_jobrunner](queue_job_cron_jobrunner/) | 18.0.1.0.1 |
| Run jobs without a dedicated JobRunner
diff --git a/queue_job/README.rst b/queue_job/README.rst
index 0db8df9ca4..9dceaf165c 100644
--- a/queue_job/README.rst
+++ b/queue_job/README.rst
@@ -11,7 +11,7 @@ Job Queue
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:19ba0a7f175cdfcf3194c4e7fa19da2919973572e627bab9bdfe73015900b64a
+ !! source digest: sha256:1087919058419188998ff826b0852e7a275b847d68679ecf8aca3c3fb73ddbac
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index af68bb2660..e8360a1800 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -2,7 +2,7 @@
{
"name": "Job Queue",
- "version": "18.0.2.0.10",
+ "version": "18.0.2.1.0",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py
index ecdff1b8eb..c98f6305df 100644
--- a/queue_job/controllers/main.py
+++ b/queue_job/controllers/main.py
@@ -6,6 +6,7 @@
import random
import time
import traceback
+from contextlib import contextmanager
from io import StringIO
from psycopg2 import OperationalError, errorcodes
@@ -26,6 +27,29 @@
DEPENDS_MAX_TRIES_ON_CONCURRENCY_FAILURE = 5
+@contextmanager
+def _prevent_commit(cr):
+ """Context manager to prevent commits on a cursor.
+
+ Commiting while the job is not finished would release the job lock, causing
+ it to be started again by the dead jobs requeuer.
+ """
+
+ def forbidden_commit(*args, **kwargs):
+ raise RuntimeError(
+ "Commit is forbidden in queue jobs. "
+ "If the current job is a cron running as queue job, "
+ "modify it to run as a normal cron."
+ )
+
+ original_commit = cr.commit
+ cr.commit = forbidden_commit
+ try:
+ yield
+ finally:
+ cr.commit = original_commit
+
+
class RunJobController(http.Controller):
@classmethod
def _acquire_job(cls, env: api.Environment, job_uuid: str) -> Job | None:
@@ -69,13 +93,16 @@ def _acquire_job(cls, env: api.Environment, job_uuid: str) -> Job | None:
def _try_perform_job(cls, env, job):
"""Try to perform the job, mark it done and commit if successful."""
_logger.debug("%s started", job)
- job.perform()
- # Triggers any stored computed fields before calling 'set_done'
- # so that will be part of the 'exec_time'
- env.flush_all()
- job.set_done()
- job.store()
- env.flush_all()
+ # TODO refactor, the relation between env and job.env is not clear
+ assert env.cr is job.env.cr
+ with _prevent_commit(env.cr):
+ job.perform()
+ # Triggers any stored computed fields before calling 'set_done'
+ # so that will be part of the 'exec_time'
+ env.flush_all()
+ job.set_done()
+ job.store()
+ env.flush_all()
env.cr.commit()
_logger.debug("%s done", job)
@@ -201,6 +228,7 @@ def create_test_job(
size=1,
failure_rate=0,
job_duration=0,
+ commit_within_job=False,
):
if not http.request.env.user.has_group("base.group_erp_manager"):
raise Forbidden(_("Access Denied"))
@@ -246,6 +274,7 @@ def create_test_job(
description=description,
failure_rate=failure_rate,
job_duration=job_duration,
+ commit_within_job=commit_within_job,
)
if size > 1:
@@ -257,6 +286,7 @@ def create_test_job(
description=description,
failure_rate=failure_rate,
job_duration=job_duration,
+ commit_within_job=commit_within_job,
)
return ""
@@ -269,6 +299,7 @@ def _create_single_test_job(
size=1,
failure_rate=0,
job_duration=0,
+ commit_within_job=False,
):
delayed = (
http.request.env["queue.job"]
@@ -278,7 +309,11 @@ def _create_single_test_job(
channel=channel,
description=description,
)
- ._test_job(failure_rate=failure_rate, job_duration=job_duration)
+ ._test_job(
+ failure_rate=failure_rate,
+ job_duration=job_duration,
+ commit_within_job=commit_within_job,
+ )
)
return f"job uuid: {delayed.db_record().uuid}"
@@ -293,6 +328,7 @@ def _create_graph_test_jobs(
description="Test job",
failure_rate=0,
job_duration=0,
+ commit_within_job=False,
):
model = http.request.env["queue.job"]
current_count = 0
@@ -315,7 +351,11 @@ def _create_graph_test_jobs(
max_retries=max_retries,
channel=channel,
description="%s #%d" % (description, current_count),
- )._test_job(failure_rate=failure_rate, job_duration=job_duration)
+ )._test_job(
+ failure_rate=failure_rate,
+ job_duration=job_duration,
+ commit_within_job=commit_within_job,
+ )
)
grouping = random.choice(possible_grouping_methods)
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index b8975b878e..3d550bf33b 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -453,9 +453,11 @@ def related_action_open_record(self):
)
return action
- def _test_job(self, failure_rate=0, job_duration=0):
+ def _test_job(self, failure_rate=0, job_duration=0, commit_within_job=False):
_logger.info("Running test job.")
if random.random() <= failure_rate:
raise JobError("Job failed")
if job_duration:
time.sleep(job_duration)
+ if commit_within_job:
+ self.env.cr.commit() # pylint: disable=invalid-commit
diff --git a/queue_job/static/description/index.html b/queue_job/static/description/index.html
index ef712748bd..d9ca6fa508 100644
--- a/queue_job/static/description/index.html
+++ b/queue_job/static/description/index.html
@@ -372,7 +372,7 @@
This addon adds an integrated Job Queue to Odoo.