Skip to content

Commit 2179538

Browse files
committed
Merge PR #853 into 18.0
Signed-off-by sbidoul
2 parents c1d4c07 + 1fcb5c3 commit 2179538

File tree

2 files changed

+42
-17
lines changed

2 files changed

+42
-17
lines changed

queue_job/jobrunner/runner.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -357,23 +357,26 @@ def _query_requeue_dead_jobs(self):
357357
ELSE exc_info
358358
END)
359359
WHERE
360-
id in (
361-
SELECT
362-
queue_job_id
363-
FROM
364-
queue_job_lock
365-
WHERE
366-
queue_job_id in (
367-
SELECT
368-
id
369-
FROM
370-
queue_job
371-
WHERE
372-
state IN ('enqueued','started')
373-
AND date_enqueued <
374-
(now() AT TIME ZONE 'utc' - INTERVAL '10 sec')
375-
)
376-
FOR UPDATE SKIP LOCKED
360+
state IN ('enqueued','started')
361+
AND date_enqueued < (now() AT TIME ZONE 'utc' - INTERVAL '10 sec')
362+
AND (
363+
id in (
364+
SELECT
365+
queue_job_id
366+
FROM
367+
queue_job_lock
368+
WHERE
369+
queue_job_lock.queue_job_id = queue_job.id
370+
FOR UPDATE SKIP LOCKED
371+
)
372+
OR NOT EXISTS (
373+
SELECT
374+
1
375+
FROM
376+
queue_job_lock
377+
WHERE
378+
queue_job_lock.queue_job_id = queue_job.id
379+
)
377380
)
378381
RETURNING uuid
379382
"""
@@ -396,6 +399,12 @@ def requeue_dead_jobs(self):
396399
However, when the Odoo server crashes or is otherwise force-stopped,
397400
running jobs are interrupted while the runner has no chance to know
398401
they have been aborted.
402+
403+
This also handles orphaned jobs (enqueued but never started, no lock).
404+
This edge case occurs when the runner marks a job as 'enqueued'
405+
but the HTTP request to start the job never reaches the Odoo server
406+
(e.g., due to server shutdown/crash between setting enqueued and
407+
the controller receiving the request).
399408
"""
400409

401410
with closing(self.conn.cursor()) as cr:

test_queue_job/tests/test_requeue_dead_job.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,19 @@ def test_requeue_dead_jobs(self):
9999

100100
uuids_requeued = self.env.cr.fetchall()
101101
self.assertTrue(queue_job.uuid in j[0] for j in uuids_requeued)
102+
103+
def test_requeue_orphaned_jobs(self):
104+
queue_job = self._get_demo_job("test_enqueued_job")
105+
job_obj = Job.load(self.env, queue_job.uuid)
106+
107+
# Only enqueued job, don't set it to started to simulate the scenario
108+
# that system shutdown before job is starting
109+
job_obj.set_enqueued()
110+
job_obj.date_enqueued = datetime.now() - timedelta(minutes=1)
111+
job_obj.store()
112+
113+
# job is now picked up by the requeue query (which includes orphaned jobs)
114+
query = Database(self.env.cr.dbname)._query_requeue_dead_jobs()
115+
self.env.cr.execute(query)
116+
uuids_requeued = self.env.cr.fetchall()
117+
self.assertTrue(queue_job.uuid in j[0] for j in uuids_requeued)

0 commit comments

Comments
 (0)