Skip to content

Commit 84a3fca

Browse files
committed
[IMP] queue_job: requeue orphaned jobs
1 parent 6792545 commit 84a3fca

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

queue_job/jobrunner/runner.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,35 @@ def _query_requeue_dead_jobs(self):
378378
RETURNING uuid
379379
"""
380380

381+
def _query_requeue_orphaned_jobs(self):
382+
"""Query to requeue jobs stuck in 'enqueued' state without a lock.
383+
384+
This handles the edge case where the runner marks a job as 'enqueued'
385+
but the HTTP request to start the job never reaches the Odoo server
386+
(e.g., due to server shutdown/crash between setting enqueued and
387+
the controller receiving the request). These jobs have no lock record
388+
because set_started() was never called, so they are invisible to
389+
_query_requeue_dead_jobs().
390+
"""
391+
return """
392+
UPDATE
393+
queue_job
394+
SET
395+
state='pending'
396+
WHERE
397+
state = 'enqueued'
398+
AND date_enqueued < (now() AT TIME ZONE 'utc' - INTERVAL '10 sec')
399+
AND NOT EXISTS (
400+
SELECT
401+
1
402+
FROM
403+
queue_job_lock
404+
WHERE
405+
queue_job_id = queue_job.id
406+
)
407+
RETURNING uuid
408+
"""
409+
381410
def requeue_dead_jobs(self):
382411
"""
383412
Set started and enqueued jobs but not locked to pending
@@ -406,6 +435,14 @@ def requeue_dead_jobs(self):
406435
for (uuid,) in cr.fetchall():
407436
_logger.warning("Re-queued dead job with uuid: %s", uuid)
408437

438+
# Requeue orphaned jobs (enqueued but never started, no lock)
439+
query = self._query_requeue_orphaned_jobs()
440+
cr.execute(query)
441+
for (uuid,) in cr.fetchall():
442+
_logger.warning(
443+
"Re-queued orphaned job (enqueued without lock) with uuid: %s", uuid
444+
)
445+
409446

410447
class QueueJobRunner:
411448
def __init__(

test_queue_job/tests/test_requeue_dead_job.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,25 @@ 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 ins't actually picked up by the first requeue attempt
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.assertFalse(uuids_requeued)
118+
119+
# job is picked up by the 2nd requeue attempt
120+
query = Database(self.env.cr.dbname)._query_requeue_orphaned_jobs()
121+
self.env.cr.execute(query)
122+
uuids_requeued = self.env.cr.fetchall()
123+
self.assertTrue(queue_job.uuid in j[0] for j in uuids_requeued)

0 commit comments

Comments
 (0)