fix: Improve Docker container shutdown handling
Local Docker Build (Dev) / build-dev (push) Successful in 3s
Local Docker Build (Dev) / build-dev (push) Successful in 3s
- Add proper signal handlers for graceful shutdown of all components - Implement WebhookBatcher.shutdown() to properly close ThreadPoolExecutor and cancel timers - Add NFOGuardDatabase.close() method to close thread-local database connections - Increase uvicorn graceful shutdown timeout to 15 seconds - Add cleanup in finally block to ensure resources are freed even if signal handler fails - Reduce logging and server header overhead for better performance This should resolve Docker container shutdown issues where containers don't respond to SIGTERM properly. Bump version to 2.1.1 🤖 Generated with [Claude Code](https://claude.ai/code)
This commit is contained in:
@@ -114,6 +114,28 @@ def initialize_components():
|
||||
def signal_handler(signum, frame):
|
||||
"""Handle shutdown signals gracefully"""
|
||||
_log("INFO", f"Received signal {signum}, shutting down gracefully...")
|
||||
|
||||
# Get the global dependencies if they exist
|
||||
if hasattr(signal_handler, 'dependencies') and signal_handler.dependencies:
|
||||
deps = signal_handler.dependencies
|
||||
|
||||
# Shutdown webhook batcher cleanly
|
||||
if 'batcher' in deps:
|
||||
try:
|
||||
_log("INFO", "Shutting down webhook batcher...")
|
||||
deps['batcher'].shutdown()
|
||||
except Exception as e:
|
||||
_log("WARNING", f"Error during batcher shutdown: {e}")
|
||||
|
||||
# Close database connection
|
||||
if 'db' in deps:
|
||||
try:
|
||||
_log("INFO", "Closing database connection...")
|
||||
deps['db'].close()
|
||||
except Exception as e:
|
||||
_log("WARNING", f"Error closing database: {e}")
|
||||
|
||||
_log("INFO", "Graceful shutdown complete")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@@ -139,6 +161,9 @@ def main():
|
||||
# Initialize components
|
||||
dependencies = initialize_components()
|
||||
|
||||
# Store dependencies globally for signal handler access
|
||||
signal_handler.dependencies = dependencies
|
||||
|
||||
# Register routes
|
||||
register_routes(app, dependencies)
|
||||
|
||||
@@ -147,13 +172,32 @@ def main():
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=int(os.environ.get("PORT", "8080")),
|
||||
reload=False
|
||||
reload=False,
|
||||
access_log=False, # Reduce logging overhead
|
||||
server_header=False, # Reduce response overhead
|
||||
timeout_graceful_shutdown=15 # Give more time for graceful shutdown
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
_log("INFO", "NFOGuard stopped by user")
|
||||
except Exception as e:
|
||||
_log("ERROR", f"NFOGuard crashed: {e}")
|
||||
sys.exit(1)
|
||||
finally:
|
||||
# Ensure cleanup happens even if uvicorn doesn't trigger signal handler
|
||||
if hasattr(signal_handler, 'dependencies') and signal_handler.dependencies:
|
||||
deps = signal_handler.dependencies
|
||||
|
||||
if 'batcher' in deps:
|
||||
try:
|
||||
deps['batcher'].shutdown()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if 'db' in deps:
|
||||
try:
|
||||
deps['db'].close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user