fix: Improve Docker container shutdown handling
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:
2025-10-12 13:02:07 -04:00
parent c8fde41dfb
commit 867bd008c5
4 changed files with 86 additions and 4 deletions
+45 -1
View File
@@ -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__":