From 39d8ae3688510f9c5f04465a0d4bf4591b73e478 Mon Sep 17 00:00:00 2001 From: Vitaly Markov Date: Fri, 13 Jul 2018 13:33:40 +0100 Subject: [PATCH] Add basic orphan handling for child processes --- pyexasol/http_transport.py | 14 +++++++++++++- pyexasol/script_output.py | 5 +++++ pyexasol/utils.py | 13 +++++++++++++ pyexasol/version.py | 2 +- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/pyexasol/http_transport.py b/pyexasol/http_transport.py index dbcf03f..c286806 100644 --- a/pyexasol/http_transport.py +++ b/pyexasol/http_transport.py @@ -239,7 +239,11 @@ def send_proxy(self): sys.stdout.buffer.flush() def handle_request(self): - self.server.handle_request() + # Wait for exactly one connection + while self.server.total_clients == 0: + self.server.handle_request() + utils.check_orphaned() + self.server.server_close() @@ -249,6 +253,8 @@ class ExaTCPServer(TCPServer): Instead of listening for incoming connections it connects to Exasol and uses proxy magic It allows to bypass various connectivity problems (e.g. firewall) """ + timeout = 5 + def __init__(self, *args, **kwargs): self.proxy_host = None self.proxy_port = None @@ -257,6 +263,8 @@ def __init__(self, *args, **kwargs): self.compression = kwargs.pop('compression', False) self.encryption = kwargs.pop('encryption', False) + self.total_clients = 0 + super().__init__(*args, **kwargs) def set_pipe(self, pipe): @@ -293,6 +301,10 @@ class ExaHTTPRequestHandler(BaseHTTPRequestHandler): def log_message(self, format, *args): pass + def setup(self): + super().setup() + self.server.total_clients += 1 + def do_PUT(self): # Compressed data loop if self.server.compression: diff --git a/pyexasol/script_output.py b/pyexasol/script_output.py index a48a370..ff65e08 100644 --- a/pyexasol/script_output.py +++ b/pyexasol/script_output.py @@ -36,6 +36,8 @@ import subprocess import pathlib +from . import utils + class ExaScriptOutputProcess(object): def __init__(self, host, port, output_dir=None): @@ -130,6 +132,9 @@ class ExaScriptOutputServer(socketserver.ThreadingMixIn, socketserver.TCPServer) def get_output_address(self): return f"{socket.getfqdn()}:{self.socket.getsockname()[1]}" + def service_actions(self): + utils.check_orphaned() + class ExaScriptOutputHandler(socketserver.StreamRequestHandler): def setup(self): diff --git a/pyexasol/utils.py b/pyexasol/utils.py index b57cc6f..76a23a9 100644 --- a/pyexasol/utils.py +++ b/pyexasol/utils.py @@ -6,6 +6,7 @@ import tempfile import socket import ssl +import os from . import constant @@ -105,3 +106,15 @@ def generate_adhoc_ssl_context(): context.load_cert_chain(certfile=cert_file.name, keyfile=key_file.name) return context + + +def check_orphaned(): + """ + Raise exception if current process is "orphaned" (parent process is dead) + It is useful to stop PyEXASOL HTTP servers from being stuck in process list when parent process was killed + + Currently it works only for Unix + Please let me know if you know a good way to detect orphans on Windows + """ + if os.getppid() == 1: + raise RuntimeError("Current process is orphaned, ppid=1") diff --git a/pyexasol/version.py b/pyexasol/version.py index 4aa8557..7b66a98 100644 --- a/pyexasol/version.py +++ b/pyexasol/version.py @@ -1 +1 @@ -__version__ = '0.3.29' +__version__ = '0.3.30'