feat: add process group management to prevent hanging subprocesses

- 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 <noreply@anthropic.com>
This commit is contained in:
Andy Lee
2025-08-10 22:11:12 +00:00
parent 3d67205670
commit 4772a5bb18

View File

@@ -1,6 +1,7 @@
import atexit import atexit
import logging import logging
import os import os
import signal
import socket import socket
import subprocess import subprocess
import sys import sys
@@ -311,6 +312,7 @@ class EmbeddingServerManager:
cwd=project_root, cwd=project_root,
stdout=None, # Direct to console stdout=None, # Direct to console
stderr=None, # Direct to console stderr=None, # Direct to console
start_new_session=True, # Create new process group for better cleanup
) )
self.server_port = port self.server_port = port
logger.info(f"Server process started with PID: {self.server_process.pid}") logger.info(f"Server process started with PID: {self.server_process.pid}")
@@ -352,7 +354,14 @@ class EmbeddingServerManager:
logger.info( logger.info(
f"Terminating server process (PID: {self.server_process.pid}) for backend {self.backend_module_name}..." 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: try:
self.server_process.wait(timeout=3) self.server_process.wait(timeout=3)
@@ -361,7 +370,13 @@ class EmbeddingServerManager:
logger.warning( logger.warning(
f"Server process {self.server_process.pid} did not terminate gracefully within 3 seconds, killing it." 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: try:
self.server_process.wait(timeout=2) self.server_process.wait(timeout=2)
logger.info(f"Server process {self.server_process.pid} killed successfully.") logger.info(f"Server process {self.server_process.pid} killed successfully.")