[FIX] core: allow signal handlers in multiprocess workers
Because Python only runs signal handlers on the main thread, native calls (IO or accept(2)) can delay these handlers running. This is especially problematic when one such call is completely stuck and we're trying to dump the stack to diagnose the issue. By running the actual worker's processing in a sub-thread and leaving the main thread sleeping, worker processes should always be able to handle signals. closes odoo/odoo#30688
This commit is contained in:
+19
-6
@@ -685,7 +685,7 @@ class Worker(object):
|
||||
if e.args[0] not in [errno.EINTR]:
|
||||
raise
|
||||
|
||||
def process_limit(self):
|
||||
def check_limits(self):
|
||||
# If our parent changed sucide
|
||||
if self.ppid != os.getppid():
|
||||
_logger.info("Worker (%s) Parent changed", self.pid)
|
||||
@@ -704,6 +704,7 @@ class Worker(object):
|
||||
soft, hard = resource.getrlimit(resource.RLIMIT_AS)
|
||||
resource.setrlimit(resource.RLIMIT_AS, (config['limit_memory_hard'], hard))
|
||||
|
||||
def set_limits(self):
|
||||
# SIGXCPU (exceeded CPU time) signal handler will raise an exception.
|
||||
r = resource.getrusage(resource.RUSAGE_SELF)
|
||||
cpu_time = r.ru_utime + r.ru_stime
|
||||
@@ -736,17 +737,18 @@ class Worker(object):
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
signal.set_wakeup_fd(self.wakeup_fd_w)
|
||||
|
||||
self.set_limits()
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.start()
|
||||
while self.alive:
|
||||
self.process_limit()
|
||||
self.multi.pipe_ping(self.watchdog_pipe)
|
||||
self.sleep()
|
||||
self.process_work()
|
||||
t = threading.Thread(name="Worker %s (%s) workthread" % (self.__class__.__name__, self.pid), target=self._runloop)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
t.join()
|
||||
_logger.info("Worker (%s) exiting. request_count: %s, registry count: %s.",
|
||||
self.pid, self.request_count,
|
||||
len(odoo.modules.registry.Registry.registries))
|
||||
@@ -756,6 +758,17 @@ class Worker(object):
|
||||
# should we use 3 to abort everything ?
|
||||
sys.exit(1)
|
||||
|
||||
def _runloop(self):
|
||||
try:
|
||||
while self.alive:
|
||||
self.multi.pipe_ping(self.watchdog_pipe)
|
||||
self.sleep()
|
||||
self.process_work()
|
||||
self.check_limits()
|
||||
except:
|
||||
_logger.exception("Worker %s (%s) Exception occured, exiting...", self.__class__.__name__, self.pid)
|
||||
sys.exit(1)
|
||||
|
||||
class WorkerHTTP(Worker):
|
||||
""" HTTP Request workers """
|
||||
def process_request(self, client, addr):
|
||||
|
||||
Reference in New Issue
Block a user