From b21ce127905dbf6adbc11f1793779665f86f8c2f Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 1 Oct 2020 17:25:48 +0200 Subject: [PATCH] Store worker's PID when a job is started Add a field in the "queue_job" model to store the worker pid. No usage for new besides "logging" for debugging (helps to find which process to investigate in case a job is stuck). Later, it may be useful to improve the recovery of job that stay in the wrong state when the job runner restarts. Closes #177 --- queue_job/job.py | 7 +++++++ queue_job/models/queue_job.py | 1 + queue_job/views/queue_job_views.xml | 1 + test_queue_job/tests/test_job.py | 16 ++++++++++++++++ 4 files changed, 25 insertions(+) diff --git a/queue_job/job.py b/queue_job/job.py index a7b7912981..8dfece42fb 100644 --- a/queue_job/job.py +++ b/queue_job/job.py @@ -5,6 +5,7 @@ import hashlib import inspect import logging +import os import sys import uuid from datetime import datetime, timedelta @@ -314,6 +315,7 @@ def _load_from_db_record(cls, job_db_record): if stored.company_id: job_.company_id = stored.company_id.id job_.identity_key = stored.identity_key + job_.worker_pid = stored.worker_pid return job_ def job_record_with_same_identity_key(self): @@ -498,6 +500,7 @@ def __init__( self._eta = None self.eta = eta self.channel = channel + self.worker_pid = None def perform(self): """Execute the job. @@ -541,6 +544,7 @@ def store(self): "date_done": False, "eta": False, "identity_key": False, + "worker_pid": self.worker_pid, } if self.date_enqueued: @@ -642,6 +646,7 @@ def set_pending(self, result=None, reset_retry=True): self.state = PENDING self.date_enqueued = None self.date_started = None + self.worker_pid = None if reset_retry: self.retry = 0 if result is not None: @@ -651,10 +656,12 @@ def set_enqueued(self): self.state = ENQUEUED self.date_enqueued = datetime.now() self.date_started = None + self.worker_pid = None def set_started(self): self.state = STARTED self.date_started = datetime.now() + self.worker_pid = os.getpid() def set_done(self, result=None): self.state = DONE diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py index dec0fcfa1d..6b3c7d7b33 100644 --- a/queue_job/models/queue_job.py +++ b/queue_job/models/queue_job.py @@ -81,6 +81,7 @@ class QueueJob(models.Model): ) identity_key = fields.Char() + worker_pid = fields.Integer() def init(self): self._cr.execute( diff --git a/queue_job/views/queue_job_views.xml b/queue_job/views/queue_job_views.xml index 8a48ae950c..7a083b3648 100644 --- a/queue_job/views/queue_job_views.xml +++ b/queue_job/views/queue_job_views.xml @@ -49,6 +49,7 @@ groups="base.group_multi_company" /> + diff --git a/test_queue_job/tests/test_job.py b/test_queue_job/tests/test_job.py index d16555e14f..e55636362a 100644 --- a/test_queue_job/tests/test_job.py +++ b/test_queue_job/tests/test_job.py @@ -134,6 +134,20 @@ def test_set_started(self): self.assertEquals(job_a.state, STARTED) self.assertEquals(job_a.date_started, datetime(2015, 3, 15, 16, 41, 0)) + def test_worker_pid(self): + """When a job is started, it gets the PID of the worker that starts it""" + method = self.env["res.users"].mapped + job_a = Job(method) + self.assertFalse(job_a.worker_pid) + with mock.patch("os.getpid", autospec=True) as mock_getpid: + mock_getpid.return_value = 99999 + job_a.set_started() + self.assertEqual(job_a.worker_pid, 99999) + + # reset the pid + job_a.set_pending() + self.assertFalse(job_a.worker_pid) + def test_set_done(self): job_a = Job(self.method) datetime_path = "odoo.addons.queue_job.job.datetime" @@ -180,6 +194,7 @@ def test_read(self): description="My description", ) test_job.user_id = 1 + test_job.worker_pid = 99999 # normally set on "set_start" test_job.company_id = self.env.ref("base.main_company").id test_job.store() job_read = Job.load(self.env, test_job.uuid) @@ -196,6 +211,7 @@ def test_read(self): self.assertEqual(test_job.result, job_read.result) self.assertEqual(test_job.user_id, job_read.user_id) self.assertEqual(test_job.company_id, job_read.company_id) + self.assertEqual(test_job.worker_pid, 99999) delta = timedelta(seconds=1) # DB does not keep milliseconds self.assertAlmostEqual( test_job.date_created, job_read.date_created, delta=delta