From 4772a5bb182e87f7955b2ebbc0135dec24b112d0 Mon Sep 17 00:00:00 2001 From: Andy Lee Date: Sun, 10 Aug 2025 22:11:12 +0000 Subject: [PATCH] feat: add process group management to prevent hanging subprocesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add start_new_session=True to subprocess.Popen for better isolation - Use os.killpg() to terminate entire process groups instead of single processes - Import signal module for SIGTERM/SIGKILL handling - This ensures child processes of embedding servers are also cleaned up 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/leann/embedding_server_manager.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/leann-core/src/leann/embedding_server_manager.py b/packages/leann-core/src/leann/embedding_server_manager.py index 8eda231..1d48d61 100644 --- a/packages/leann-core/src/leann/embedding_server_manager.py +++ b/packages/leann-core/src/leann/embedding_server_manager.py @@ -1,6 +1,7 @@ import atexit import logging import os +import signal import socket import subprocess import sys @@ -311,6 +312,7 @@ class EmbeddingServerManager: cwd=project_root, stdout=None, # Direct to console stderr=None, # Direct to console + start_new_session=True, # Create new process group for better cleanup ) self.server_port = port logger.info(f"Server process started with PID: {self.server_process.pid}") @@ -352,7 +354,14 @@ class EmbeddingServerManager: logger.info( f"Terminating server process (PID: {self.server_process.pid}) for backend {self.backend_module_name}..." ) - self.server_process.terminate() + + # Try terminating the whole process group first + try: + pgid = os.getpgid(self.server_process.pid) + os.killpg(pgid, signal.SIGTERM) + except Exception: + # Fallback to terminating just the process + self.server_process.terminate() try: self.server_process.wait(timeout=3) @@ -361,7 +370,13 @@ class EmbeddingServerManager: logger.warning( f"Server process {self.server_process.pid} did not terminate gracefully within 3 seconds, killing it." ) - self.server_process.kill() + # Try killing the whole process group + try: + pgid = os.getpgid(self.server_process.pid) + os.killpg(pgid, signal.SIGKILL) + except Exception: + # Fallback to killing just the process + self.server_process.kill() try: self.server_process.wait(timeout=2) logger.info(f"Server process {self.server_process.pid} killed successfully.")