Skip to content

Commit b766599

Browse files
author
Michael Hammann
committed
fix: restructured queue, added timeout parameter, depends_on works now with rpcinterfaces and autostart
1 parent 99c98de commit b766599

File tree

4 files changed

+106
-41
lines changed

4 files changed

+106
-41
lines changed

supervisor/options.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,7 @@ def get(section, opt, *args, **kwargs):
940940
raise ValueError(
941941
f"program section {section} has invalid runningregex value. Error {e}")
942942
depends_on = get(section, 'depends_on', None)
943+
spawn_timeout = int(get(section, 'spawn_timeout', 60))
943944

944945
# find uid from "user" option
945946
user = get(section, 'user', None)
@@ -1068,6 +1069,7 @@ def get(section, opt, *args, **kwargs):
10681069
serverurl=serverurl,
10691070
runningregex=runningregex,
10701071
depends_on=depends_on,
1072+
spawn_timeout=spawn_timeout,
10711073
)
10721074

10731075
programs.append(pconfig)
@@ -1886,7 +1888,8 @@ class ProcessConfig(Config):
18861888
'stderr_events_enabled', 'stderr_syslog',
18871889
'stopsignal', 'stopwaitsecs', 'stopasgroup', 'killasgroup',
18881890
'exitcodes', 'redirect_stderr' ]
1889-
optional_param_names = [ 'environment', 'serverurl', 'runningregex', 'depends_on' ]
1891+
optional_param_names = [ 'environment', 'serverurl',
1892+
'runningregex', 'depends_on', 'spawn_timeout' ]
18901893

18911894
def __init__(self, options, **params):
18921895
self.options = options

supervisor/process.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,22 @@ def record_spawnerr(self, msg):
192192
self.spawnerr = msg
193193
self.config.options.logger.info("spawnerr: %s" % msg)
194194

195+
def queue_all_dependee_processes(self, supervisor):
196+
if self.config.name not in supervisor.process_spawn_set:
197+
supervisor.process_spawn_queue.append(self)
198+
supervisor.process_spawn_set.add(self.config.name)
199+
# all dependees that are not in queue and not in STARTING need to be added to queue.
200+
if self.config.depends_on is not None:
201+
for dependee in self.config.depends_on.values():
202+
if dependee.state is not ProcessStates.RUNNING:
203+
ready_to_be_spawned = False
204+
if dependee.state is not ProcessStates.STARTING:
205+
if dependee.config.name not in supervisor.process_spawn_set:
206+
supervisor.process_spawn_queue.append(dependee)
207+
supervisor.process_spawn_set.add(dependee.config.name)
208+
dependee.queue_all_dependee_processes(supervisor)
209+
210+
195211
def spawn(self, supervisor=None):
196212
"""Start the subprocess. It must not be running already.
197213
@@ -202,14 +218,15 @@ def spawn(self, supervisor=None):
202218
# check if the process is dependent upon any other process and if so,
203219
# make sure that one is in the RUNNING state
204220
# spawn if all dependees are running - else add to queue if not in queue already
205-
if self.config.depends_on is not None and any([dependee.state is not ProcessStates.RUNNING for dependee in self.config.depends_on.values()]):
206-
# all dependees that are not in queue and not in STARTING need to be added to queue.
207-
for dependee in self.config.depends_on.values():
208-
if dependee.state is not (ProcessStates.STARTING or ProcessStates.RUNNING):
209-
if dependee.config.name not in supervisor.waiting_to_be_spawned:
210-
supervisor.process_queue.append(dependee)
211-
supervisor.waiting_to_be_spawned.add(dependee.config.name)
212-
else:
221+
ready_to_be_spawned = True
222+
if self.config.depends_on is not None:
223+
# if all dependees are running - no problem process can be spawned
224+
if any([dependee.state is not ProcessStates.RUNNING for dependee in
225+
self.config.depends_on.values()]):
226+
ready_to_be_spawned = False
227+
# all dependees not in queue and not in STARTING need to be added to queue.
228+
self.queue_all_dependee_processes(supervisor)
229+
if ready_to_be_spawned:
213230
# if runningregex is used, set the log_offset correctly
214231
if self.config.runningregex is not None:
215232
try:

supervisor/rpcinterface.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,6 @@ def startProcess(self, name, wait=True):
285285
"""
286286
## check if the process is dependent upon any other process and if so make sure that one is in the RUNNING state
287287
group, process = self._getGroupAndProcess(name)
288-
if process.config.depends_on is not None:
289-
self.startDependeeProcesses(process)
290-
291288

292289
self._update('startProcess')
293290
group, process = self._getGroupAndProcess(name)
@@ -312,6 +309,10 @@ def startProcess(self, name, wait=True):
312309
"%s is in an unknown process state" % name)
313310

314311
process.spawn(self.supervisord)
312+
# if process has dependees, return succesfull start -
313+
# errors will be handled in main loop and inside process.spawn()
314+
if process.config.depends_on is not None:
315+
return True
315316

316317
# We call reap() in order to more quickly obtain the side effects of
317318
# process.finish(), which reap() eventually ends up calling. This

supervisor/supervisord.py

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ def __init__(self, options):
5959
self.options = options
6060
self.process_groups = {}
6161
self.ticks = {}
62-
self.process_queue = deque()
63-
self.waiting_to_be_spawned = set()
62+
self.process_spawn_queue = deque()
63+
self.process_spawn_set = set()
6464

6565
def main(self):
6666
if not self.options.first:
@@ -270,33 +270,7 @@ def runforever(self):
270270
for group in pgroups:
271271
group.transition(self)
272272

273-
# check for processes that are sheduled but not started
274-
if self.process_queue:
275-
# spawn if all dependencies are running or process has no dependencies
276-
if self.process_queue[-1].config.depends_on is not None:
277-
if all([dependee.get_state() == ProcessStates.RUNNING for dependee in self.process_queue[-1].config.depends_on.values()]):
278-
ready_process = self.process_queue.pop()
279-
ready_process.spawn(self)
280-
try:
281-
self.waiting_to_be_spawned.remove(ready_process.config.name)
282-
except KeyError:
283-
pass
284-
# add any dependee which is not RUNNING or STARTING to queue
285-
else:
286-
for dependee in self.process_queue[-1].config.depends_on.values():
287-
if dependee.state is not (ProcessStates.STARTING or ProcessStates.RUNNING):
288-
# Should we check if the process is already in the queue?
289-
if dependee.config.name not in self.waiting_to_be_spawned:
290-
self.process_queue.append(dependee)
291-
self.waiting_to_be_spawned.add(dependee.config.name)
292-
293-
else:
294-
ready_process = self.process_queue.pop()
295-
ready_process.spawn(self)
296-
try:
297-
self.waiting_to_be_spawned.remove(ready_process.config.name)
298-
except KeyError:
299-
pass
273+
self.spawn_dependee_queue()
300274

301275
self.reap()
302276
self.handle_signal()
@@ -373,6 +347,76 @@ def handle_signal(self):
373347
def get_state(self):
374348
return self.options.mood
375349

350+
def spawn_dependee_queue(self):
351+
"""
352+
check for processes that are sheduled but not started
353+
if the queue is well ordered just pop and spawn all that are ready,
354+
Currently ordering is not considered when adding to queue -
355+
therefore just iterate over queue and spawn all ready processes.
356+
"""
357+
if self.process_spawn_queue:
358+
for i in range(len(self.process_spawn_queue)-1, -1, -1):
359+
if self.process_spawn_queue[i].config.depends_on is not None:
360+
if any([dependee.state is ProcessStates.FATAL for dependee in
361+
self.process_spawn_queue[i].config.depends_on.values()]):
362+
for process in self.process_spawn_queue:
363+
process.record_spawnerr(
364+
'Dependee process did not start - set FATAL state for {}'
365+
.format(process.config.name))
366+
process.change_state(ProcessStates.FATAL)
367+
self.process_spawn_set = set()
368+
self.process_spawn_queue.clear()
369+
break
370+
if all([dependee.state is ProcessStates.RUNNING for dependee in
371+
self.process_spawn_queue[i].config.depends_on.values()]):
372+
self.spawn_me = self.process_spawn_queue[i]
373+
del self.process_spawn_queue[i]
374+
self.spawn_me.spawn(self)
375+
self.last_spawned = time.time()
376+
self.last_print = time.time()
377+
else:
378+
self.spawn_me = self.process_spawn_queue[i]
379+
del self.process_spawn_queue[i]
380+
self.spawn_me.spawn(self)
381+
self.last_spawned = time.time()
382+
self.last_print = time.time()
383+
384+
try:
385+
if (time.time() - self.last_spawned) >= self.spawn_me.config.spawn_timeout:
386+
msg = (
387+
"timeout: dependee process {} in {} state for over {} seconds, {} are not spawned"
388+
.format(self.spawn_me.config.name,
389+
getProcessStateDescription(self.spawn_me.state),
390+
self.spawn_me.config.spawn_timeout,
391+
[process.config.name for process in self.process_spawn_queue]))
392+
self.process_spawn_queue[i].config.options.logger.warn(msg)
393+
self.spawn_me.record_spawnerr(
394+
'Process {} stayed for over {} seconds in STARTING'.format(
395+
self.spawn_me.config.name, self.spawn_me.config.spawn_timeout))
396+
self.spawn_me.change_state(ProcessStates.FATAL)
397+
for process in self.process_spawn_queue:
398+
process.record_spawnerr(
399+
'Dependee process {} did not start - set FATAL state for {}'.format(
400+
self.spawn_me.config.name, process.config.name))
401+
process.change_state(ProcessStates.FATAL)
402+
self.process_spawn_set = set()
403+
self.process_spawn_queue.clear()
404+
self.last_spawned = time.time()
405+
elif (time.time() - self.last_print) >= 5:
406+
msg = ("waiting for dependee process {} in {} state to be RUNNING"
407+
.format(self.spawn_me.config.name,
408+
getProcessStateDescription(self.spawn_me.state)))
409+
self.process_spawn_queue[0].config.options.logger.info(msg)
410+
self.last_print = time.time()
411+
except UnboundLocalError:
412+
self.last_spawned = time.time()
413+
self.last_print = time.time()
414+
415+
416+
# empty set - all processes from queue are running
417+
else:
418+
self.process_spawn_set = set()
419+
376420
def timeslice(period, when):
377421
return int(when - (when % period))
378422

0 commit comments

Comments
 (0)