File tree Expand file tree Collapse file tree 2 files changed +59
-0
lines changed
Expand file tree Collapse file tree 2 files changed +59
-0
lines changed Original file line number Diff line number Diff 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
410447class QueueJobRunner :
411448 def __init__ (
Original file line number Diff line number Diff 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 )
You can’t perform that action at this time.
0 commit comments