2
2
import multiprocessing
3
3
import os
4
4
import time
5
- import uuid
6
- from concurrent . futures import ProcessPoolExecutor
5
+ from concurrent . futures import Future , ProcessPoolExecutor
6
+ from functools import partial
7
7
8
8
from bolt .db import transaction
9
9
from bolt .logs import app_logger
@@ -28,8 +28,6 @@ def __init__(self, max_processes=None, max_jobs_per_process=None, stats_every=No
28
28
self .max_processes = self .executor ._max_workers
29
29
self .max_jobs_per_process = max_jobs_per_process
30
30
31
- self .uuid = uuid .uuid4 ()
32
-
33
31
self ._is_shutting_down = False
34
32
35
33
def run (self ):
@@ -63,37 +61,25 @@ def run(self):
63
61
job_request .source ,
64
62
)
65
63
66
- job = job_request .convert_to_job (worker_uuid = self . uuid )
64
+ job = job_request .convert_to_job ()
67
65
68
66
job_uuid = str (job .uuid ) # Make a str copy
69
67
70
68
# Release these now
71
69
del job_request
72
70
del job
73
71
74
- self .executor .submit (process_job , job_uuid )
72
+ future = self .executor .submit (process_job , job_uuid )
73
+ future .add_done_callback (partial (future_finished_callback , job_uuid ))
75
74
76
75
def shutdown (self ):
77
76
if self ._is_shutting_down :
78
77
# Already shutting down somewhere else
79
78
return
80
79
81
- self ._is_shutting_down = True
82
-
83
80
logger .info ("Job worker shutdown started" )
84
-
85
- # Make an attept to immediatelly move any unstarted jobs from this worker
86
- # to the JobResults as cancelled. If they have a retry, they'll be picked
87
- # up by the next worker process.
88
- for job in Job .objects .filter (worker_uuid = self .uuid , started_at__isnull = True ):
89
- job .convert_to_result (status = JobResultStatuses .CANCELLED )
90
-
91
- # Now shutdown the process pool.
92
- # There's still some chance of a race condition here where we
93
- # just deleted a Job and it was still picked up, but we'll log
94
- # missing jobs as warnings instead of exceptions.
81
+ self ._is_shutting_down = True
95
82
self .executor .shutdown (wait = True , cancel_futures = True )
96
-
97
83
logger .info ("Job worker shutdown complete" )
98
84
99
85
def maybe_log_stats (self ):
@@ -149,6 +135,15 @@ def check_job_results(self):
149
135
JobResult .objects .retry_failed_jobs ()
150
136
151
137
138
+ def future_finished_callback (job_uuid : str , future : Future ):
139
+ if future .cancelled ():
140
+ logger .warning ("Job cancelled job_uuid=%s" , job_uuid )
141
+ job = Job .objects .get (uuid = job_uuid )
142
+ job .convert_to_result (status = JobResultStatuses .CANCELLED )
143
+ else :
144
+ logger .debug ("Job finished job_uuid=%s" , job_uuid )
145
+
146
+
152
147
def process_job (job_uuid ):
153
148
try :
154
149
worker_pid = os .getpid ()
0 commit comments