From 1053d0c715262a8b462e31e816693cd30d41e6e2 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Mon, 25 May 2026 15:36:47 +0200
Subject: [PATCH] fixed event shutdown
---
app.py | 50 +++++++++++++++++--------------
modules/shared/eventManagement.py | 1 +
2 files changed, 28 insertions(+), 23 deletions(-)
diff --git a/app.py b/app.py
index 93cc8b79..3bb9dcc9 100644
--- a/app.py
+++ b/app.py
@@ -426,32 +426,36 @@ async def lifespan(app: FastAPI):
yield
- # --- Stop Managers ---
- eventManager.stop()
-
- # --- Stop Feature Containers (Plug&Play) ---
+ # --- Shutdown sequence (protected against CancelledError) ---
try:
- mainModules = loadFeatureMainModules()
- for featureName, module in mainModules.items():
- if hasattr(module, "onStop"):
- try:
- await module.onStop(eventUser)
- logger.info(f"Feature '{featureName}' stopped")
- except Exception as e:
- logger.error(f"Feature '{featureName}' failed to stop: {e}")
- except Exception as e:
- logger.warning(f"Could not shutdown feature containers: {e}")
+ # 1. Stop scheduler first (removes all pending cron/interval jobs)
+ eventManager.stop()
- # --- Close all PostgreSQL connection pools ---
- # Must run LAST: feature `onStop` hooks may still issue DB calls during
- # shutdown. Once we tear down the pools, no more borrows are possible.
- try:
- from modules.connectors.connectorDbPostgre import closeAllPools
- closeAllPools()
- except Exception as e:
- logger.warning(f"Closing DB connection pools failed: {e}")
+ # 2. Stop Feature Containers (Plug&Play)
+ try:
+ mainModules = loadFeatureMainModules()
+ for featureName, module in mainModules.items():
+ if hasattr(module, "onStop"):
+ try:
+ await module.onStop(eventUser)
+ logger.info(f"Feature '{featureName}' stopped")
+ except Exception as e:
+ logger.error(f"Feature '{featureName}' failed to stop: {e}")
+ except Exception as e:
+ logger.warning(f"Could not shutdown feature containers: {e}")
- logger.info("Application has been shut down")
+ # 3. Close all PostgreSQL connection pools (LAST -- features may still
+ # issue DB calls during their onStop hooks)
+ try:
+ from modules.connectors.connectorDbPostgre import closeAllPools
+ closeAllPools()
+ except Exception as e:
+ logger.warning(f"Closing DB connection pools failed: {e}")
+
+ logger.info("Application has been shut down")
+
+ except asyncio.CancelledError:
+ logger.info("Shutdown interrupted (CancelledError) -- resources released")
# Custom function to generate readable operation IDs for Swagger UI
diff --git a/modules/shared/eventManagement.py b/modules/shared/eventManagement.py
index ebbf2131..1edd53be 100644
--- a/modules/shared/eventManagement.py
+++ b/modules/shared/eventManagement.py
@@ -55,6 +55,7 @@ class EventManagement:
def stop(self) -> None:
if self._scheduler and self._scheduler.running:
try:
+ self._scheduler.remove_all_jobs()
self._scheduler.shutdown(wait=False)
logger.info("EventManagement scheduler stopped")
except Exception as exc: