cherry-pick 72b29659
This commit is contained in:
parent
1db4d81dbb
commit
8ebd98ae8e
3 changed files with 33 additions and 22 deletions
|
|
@ -163,3 +163,24 @@ def post_fork(server, worker):
|
||||||
uvicorn_error_logger.handlers = []
|
uvicorn_error_logger.handlers = []
|
||||||
uvicorn_error_logger.setLevel(logging.CRITICAL)
|
uvicorn_error_logger.setLevel(logging.CRITICAL)
|
||||||
uvicorn_error_logger.propagate = False
|
uvicorn_error_logger.propagate = False
|
||||||
|
|
||||||
|
|
||||||
|
def worker_exit(server, worker):
|
||||||
|
"""
|
||||||
|
Executed when a worker is about to exit.
|
||||||
|
|
||||||
|
This is called for each worker process when it exits. We should only
|
||||||
|
clean up worker-local resources here, NOT the shared Manager.
|
||||||
|
The Manager should only be shut down by the main process in on_exit().
|
||||||
|
"""
|
||||||
|
print("=" * 80)
|
||||||
|
print(f"GUNICORN WORKER PROCESS: Shutting down worker {worker.pid}")
|
||||||
|
print(f"Process ID: {os.getpid()}")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
# Clean up worker-local resources without shutting down the Manager
|
||||||
|
# Pass shutdown_manager=False to prevent Manager shutdown
|
||||||
|
finalize_share_data(shutdown_manager=False)
|
||||||
|
|
||||||
|
print(f"Worker {worker.pid} cleanup complete")
|
||||||
|
print("=" * 80)
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,11 @@ Start LightRAG server with Gunicorn
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import signal
|
|
||||||
import pipmaster as pm
|
import pipmaster as pm
|
||||||
from lightrag.api.utils_api import display_splash_screen, check_env_file
|
from lightrag.api.utils_api import display_splash_screen, check_env_file
|
||||||
from lightrag.api.config import global_args
|
from lightrag.api.config import global_args
|
||||||
from lightrag.utils import get_env_value
|
from lightrag.utils import get_env_value
|
||||||
from lightrag.kg.shared_storage import initialize_share_data, finalize_share_data
|
from lightrag.kg.shared_storage import initialize_share_data
|
||||||
|
|
||||||
from lightrag.constants import (
|
from lightrag.constants import (
|
||||||
DEFAULT_WOKERS,
|
DEFAULT_WOKERS,
|
||||||
|
|
@ -34,20 +33,6 @@ def check_and_install_dependencies():
|
||||||
print(f"{package} installed successfully")
|
print(f"{package} installed successfully")
|
||||||
|
|
||||||
|
|
||||||
# Signal handler for graceful shutdown
|
|
||||||
def signal_handler(sig, frame):
|
|
||||||
print("\n\n" + "=" * 80)
|
|
||||||
print("RECEIVED TERMINATION SIGNAL")
|
|
||||||
print(f"Process ID: {os.getpid()}")
|
|
||||||
print("=" * 80 + "\n")
|
|
||||||
|
|
||||||
# Release shared resources
|
|
||||||
finalize_share_data()
|
|
||||||
|
|
||||||
# Exit with success status
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Set Gunicorn mode flag for lifespan cleanup detection
|
# Set Gunicorn mode flag for lifespan cleanup detection
|
||||||
os.environ["LIGHTRAG_GUNICORN_MODE"] = "1"
|
os.environ["LIGHTRAG_GUNICORN_MODE"] = "1"
|
||||||
|
|
@ -59,9 +44,10 @@ def main():
|
||||||
# Check and install dependencies
|
# Check and install dependencies
|
||||||
check_and_install_dependencies()
|
check_and_install_dependencies()
|
||||||
|
|
||||||
# Register signal handlers for graceful shutdown
|
# Note: Signal handlers are NOT registered here because:
|
||||||
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C
|
# - Worker cleanup is handled by gunicorn_config.worker_exit()
|
||||||
signal.signal(signal.SIGTERM, signal_handler) # kill command
|
# - Master cleanup is handled by gunicorn_config.on_exit()
|
||||||
|
# This prevents race conditions when multiple processes try to finalize shared data
|
||||||
|
|
||||||
# Display startup information
|
# Display startup information
|
||||||
display_splash_screen(global_args)
|
display_splash_screen(global_args)
|
||||||
|
|
|
||||||
|
|
@ -1565,7 +1565,7 @@ def get_namespace_lock(
|
||||||
return NamespaceLock(namespace, workspace, enable_logging)
|
return NamespaceLock(namespace, workspace, enable_logging)
|
||||||
|
|
||||||
|
|
||||||
def finalize_share_data():
|
def finalize_share_data(shutdown_manager: bool = True):
|
||||||
"""
|
"""
|
||||||
Release shared resources and clean up.
|
Release shared resources and clean up.
|
||||||
|
|
||||||
|
|
@ -1574,6 +1574,10 @@ def finalize_share_data():
|
||||||
|
|
||||||
In multi-process mode, it shuts down the Manager and releases all shared objects.
|
In multi-process mode, it shuts down the Manager and releases all shared objects.
|
||||||
In single-process mode, it simply resets the global variables.
|
In single-process mode, it simply resets the global variables.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shutdown_manager: If True, shut down the multiprocessing Manager.
|
||||||
|
Should be True only for the main process, False for worker processes.
|
||||||
"""
|
"""
|
||||||
global \
|
global \
|
||||||
_manager, \
|
_manager, \
|
||||||
|
|
@ -1598,8 +1602,8 @@ def finalize_share_data():
|
||||||
f"Process {os.getpid()} finalizing storage data (multiprocess={_is_multiprocess})"
|
f"Process {os.getpid()} finalizing storage data (multiprocess={_is_multiprocess})"
|
||||||
)
|
)
|
||||||
|
|
||||||
# In multi-process mode, shut down the Manager
|
# In multi-process mode, shut down the Manager only if requested
|
||||||
if _is_multiprocess and _manager is not None:
|
if _is_multiprocess and _manager is not None and shutdown_manager:
|
||||||
try:
|
try:
|
||||||
# Clear shared resources before shutting down Manager
|
# Clear shared resources before shutting down Manager
|
||||||
if _shared_dicts is not None:
|
if _shared_dicts is not None:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue