🎉 Improved logging system & documentation updates 🎉
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
backend/utils/__pycache__/timer_manager.cpython-313.pyc
Normal file
BIN
backend/utils/__pycache__/timer_manager.cpython-313.pyc
Normal file
Binary file not shown.
647
backend/utils/timeout_force_quit_manager.py
Normal file
647
backend/utils/timeout_force_quit_manager.py
Normal file
@ -0,0 +1,647 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Timeout Force-Quit Manager mit Terminal-Countdown
|
||||
|
||||
Spezialiserter Manager für Force-Quit-Timeouts mit visueller Terminal-Anzeige
|
||||
und robuster Datenbankbereinigung (WAL/SHM-Dateien).
|
||||
|
||||
Funktionen:
|
||||
- Terminal-Countdown mit Fortschrittsbalken
|
||||
- Automatische Datenbankbereinigung
|
||||
- Force-Quit bei Timeout
|
||||
- Integration mit bestehendem Timer-System
|
||||
- Robuste WAL/SHM-Dateibereinigung
|
||||
|
||||
Autor: System
|
||||
Erstellt: 2025
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import signal
|
||||
import shutil
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, Callable, Dict, Any
|
||||
from contextlib import contextmanager
|
||||
|
||||
# Logging
|
||||
try:
|
||||
from utils.logging_config import get_logger
|
||||
logger = get_logger("timeout_force_quit")
|
||||
except ImportError:
|
||||
import logging
|
||||
logger = logging.getLogger("timeout_force_quit")
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
# Timer-System Integration
|
||||
try:
|
||||
from utils.timer_manager import (
|
||||
get_timer_manager, TimerType, ForceQuitAction, TimerStatus
|
||||
)
|
||||
from models import SystemTimer, get_cached_session
|
||||
TIMER_SYSTEM_AVAILABLE = True
|
||||
except ImportError:
|
||||
logger.warning("Timer-System nicht verfügbar - verwende Fallback-Implementation")
|
||||
TIMER_SYSTEM_AVAILABLE = False
|
||||
|
||||
# Datenbank-Cleanup
|
||||
try:
|
||||
from utils.database_cleanup import safe_database_cleanup
|
||||
DATABASE_CLEANUP_AVAILABLE = True
|
||||
except ImportError:
|
||||
logger.warning("Database-Cleanup-Manager nicht verfügbar - verwende Basis-Cleanup")
|
||||
DATABASE_CLEANUP_AVAILABLE = False
|
||||
|
||||
|
||||
class TimeoutForceQuitManager:
|
||||
"""
|
||||
Manager für Timeout-basierte Force-Quit-Operationen mit Terminal-Countdown.
|
||||
|
||||
Bietet:
|
||||
- Visueller Terminal-Countdown
|
||||
- Automatische Datenbankbereinigung
|
||||
- Robuste WAL/SHM-Dateibereinigung
|
||||
- Konfigurierbare Timeout-Aktionen
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
timeout_seconds: int = 45,
|
||||
warning_seconds: int = 15,
|
||||
database_cleanup: bool = True,
|
||||
force_wal_cleanup: bool = True):
|
||||
"""
|
||||
Initialisiert den Timeout Force-Quit Manager.
|
||||
|
||||
Args:
|
||||
timeout_seconds: Gesamttimeout in Sekunden
|
||||
warning_seconds: Warnzeit vor Force-Quit in Sekunden
|
||||
database_cleanup: Datenbankbereinigung aktivieren
|
||||
force_wal_cleanup: Aggressive WAL/SHM-Bereinigung
|
||||
"""
|
||||
self.timeout_seconds = timeout_seconds
|
||||
self.warning_seconds = warning_seconds
|
||||
self.database_cleanup = database_cleanup
|
||||
self.force_wal_cleanup = force_wal_cleanup
|
||||
|
||||
# Countdown-Status
|
||||
self.is_active = False
|
||||
self.start_time = None
|
||||
self.timer_thread = None
|
||||
self.countdown_thread = None
|
||||
self.shutdown_callback: Optional[Callable] = None
|
||||
|
||||
# Terminal-Kontrolle
|
||||
self.show_terminal_countdown = True
|
||||
self.terminal_lock = threading.Lock()
|
||||
|
||||
logger.info(f"🔧 Timeout Force-Quit Manager initialisiert - Timeout: {timeout_seconds}s, Warnung: {warning_seconds}s")
|
||||
|
||||
def set_shutdown_callback(self, callback: Callable):
|
||||
"""Setzt eine Callback-Funktion für den Shutdown"""
|
||||
self.shutdown_callback = callback
|
||||
logger.debug("Shutdown-Callback registriert")
|
||||
|
||||
def start_timeout(self, reason: str = "System-Timeout") -> bool:
|
||||
"""
|
||||
Startet den Timeout-Countdown.
|
||||
|
||||
Args:
|
||||
reason: Grund für den Timeout
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich gestartet
|
||||
"""
|
||||
if self.is_active:
|
||||
logger.warning("Timeout bereits aktiv")
|
||||
return False
|
||||
|
||||
try:
|
||||
self.is_active = True
|
||||
self.start_time = datetime.now()
|
||||
|
||||
logger.warning(f"🚨 TIMEOUT GESTARTET - {reason}")
|
||||
logger.warning(f"⏱️ Force-Quit in {self.timeout_seconds} Sekunden")
|
||||
|
||||
# Timer für Force-Quit
|
||||
self.timer_thread = threading.Thread(
|
||||
target=self._timeout_worker,
|
||||
args=(reason,),
|
||||
name="TimeoutForceQuit-Timer",
|
||||
daemon=True
|
||||
)
|
||||
self.timer_thread.start()
|
||||
|
||||
# Terminal-Countdown (nur wenn stdout verfügbar)
|
||||
if self.show_terminal_countdown and sys.stdout.isatty():
|
||||
self.countdown_thread = threading.Thread(
|
||||
target=self._terminal_countdown_worker,
|
||||
name="TimeoutForceQuit-Countdown",
|
||||
daemon=True
|
||||
)
|
||||
self.countdown_thread.start()
|
||||
|
||||
# Integration mit Timer-System falls verfügbar
|
||||
if TIMER_SYSTEM_AVAILABLE:
|
||||
self._create_system_timer(reason)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler beim Starten des Timeouts: {e}")
|
||||
self.is_active = False
|
||||
return False
|
||||
|
||||
def cancel_timeout(self) -> bool:
|
||||
"""
|
||||
Bricht den laufenden Timeout ab.
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich abgebrochen
|
||||
"""
|
||||
if not self.is_active:
|
||||
return False
|
||||
|
||||
try:
|
||||
self.is_active = False
|
||||
|
||||
logger.info("✅ Timeout abgebrochen")
|
||||
|
||||
# Terminal-Ausgabe löschen
|
||||
if self.show_terminal_countdown and sys.stdout.isatty():
|
||||
with self.terminal_lock:
|
||||
print("\r" + " " * 80 + "\r", end="", flush=True)
|
||||
print("✅ Timeout abgebrochen")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler beim Abbrechen des Timeouts: {e}")
|
||||
return False
|
||||
|
||||
def extend_timeout(self, additional_seconds: int) -> bool:
|
||||
"""
|
||||
Verlängert den laufenden Timeout.
|
||||
|
||||
Args:
|
||||
additional_seconds: Zusätzliche Sekunden
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich verlängert
|
||||
"""
|
||||
if not self.is_active:
|
||||
logger.warning("Kein aktiver Timeout zum Verlängern")
|
||||
return False
|
||||
|
||||
try:
|
||||
self.timeout_seconds += additional_seconds
|
||||
logger.info(f"⏰ Timeout um {additional_seconds} Sekunden verlängert")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler beim Verlängern des Timeouts: {e}")
|
||||
return False
|
||||
|
||||
def _timeout_worker(self, reason: str):
|
||||
"""Worker-Thread für den eigentlichen Timeout"""
|
||||
try:
|
||||
# Warte bis zum Timeout
|
||||
time.sleep(self.timeout_seconds)
|
||||
|
||||
if self.is_active:
|
||||
logger.critical(f"🚨 FORCE-QUIT TIMEOUT ERREICHT - {reason}")
|
||||
self._execute_force_quit()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler im Timeout-Worker: {e}")
|
||||
|
||||
def _terminal_countdown_worker(self):
|
||||
"""Worker-Thread für den visuellen Terminal-Countdown"""
|
||||
try:
|
||||
while self.is_active:
|
||||
elapsed = (datetime.now() - self.start_time).total_seconds()
|
||||
remaining = max(0, self.timeout_seconds - elapsed)
|
||||
|
||||
if remaining <= 0:
|
||||
break
|
||||
|
||||
# Fortschrittsbalken und Countdown
|
||||
progress = 1.0 - (remaining / self.timeout_seconds)
|
||||
bar_width = 40
|
||||
filled_width = int(bar_width * progress)
|
||||
|
||||
# Warnung-Status
|
||||
is_warning = remaining <= self.warning_seconds
|
||||
warning_icon = "🚨" if is_warning else "⏳"
|
||||
|
||||
# Terminal-Ausgabe mit Lock
|
||||
with self.terminal_lock:
|
||||
bar = "█" * filled_width + "░" * (bar_width - filled_width)
|
||||
countdown_text = (
|
||||
f"\r{warning_icon} FORCE-QUIT in: {int(remaining):3d}s "
|
||||
f"[{bar}] {progress*100:6.1f}% "
|
||||
)
|
||||
print(countdown_text, end="", flush=True)
|
||||
|
||||
# Warnung ausgeben
|
||||
if is_warning and int(remaining) % 5 == 0:
|
||||
logger.warning(f"⚠️ WARNUNG: Force-Quit in {int(remaining)} Sekunden!")
|
||||
|
||||
time.sleep(0.1) # 100ms Update-Intervall
|
||||
|
||||
# Letzte Ausgabe
|
||||
if self.is_active:
|
||||
with self.terminal_lock:
|
||||
print("\r🚨 FORCE-QUIT WIRD AUSGEFÜHRT!" + " " * 30, flush=True)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler im Terminal-Countdown: {e}")
|
||||
|
||||
def _create_system_timer(self, reason: str):
|
||||
"""Erstellt einen System-Timer für Integration mit bestehendem Timer-System"""
|
||||
try:
|
||||
timer_manager = get_timer_manager()
|
||||
|
||||
timer_name = f"force_quit_{int(time.time())}"
|
||||
|
||||
timer = timer_manager.create_timer(
|
||||
name=timer_name,
|
||||
timer_type=TimerType.SYSTEM,
|
||||
duration_seconds=self.timeout_seconds,
|
||||
force_quit_action=ForceQuitAction.SHUTDOWN,
|
||||
auto_start=True,
|
||||
warning_message=f"Force-Quit wegen: {reason}",
|
||||
force_quit_warning_seconds=self.warning_seconds
|
||||
)
|
||||
|
||||
if timer:
|
||||
logger.debug(f"System-Timer '{timer_name}' erstellt")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"System-Timer konnte nicht erstellt werden: {e}")
|
||||
|
||||
def _execute_force_quit(self):
|
||||
"""Führt den Force-Quit aus"""
|
||||
try:
|
||||
logger.critical("🚨 FORCE-QUIT WIRD AUSGEFÜHRT")
|
||||
|
||||
# Terminal-Ausgabe stoppen
|
||||
self.is_active = False
|
||||
|
||||
if self.show_terminal_countdown and sys.stdout.isatty():
|
||||
with self.terminal_lock:
|
||||
print("\r🚨 FORCE-QUIT AKTIV - DATENBANKBEREINIGUNG..." + " " * 20, flush=True)
|
||||
|
||||
# 1. Shutdown-Callback ausführen (falls gesetzt)
|
||||
if self.shutdown_callback:
|
||||
try:
|
||||
logger.info("📞 Führe Shutdown-Callback aus...")
|
||||
self.shutdown_callback()
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler im Shutdown-Callback: {e}")
|
||||
|
||||
# 2. Datenbankbereinigung
|
||||
if self.database_cleanup:
|
||||
self._perform_database_cleanup()
|
||||
|
||||
# 3. System beenden
|
||||
logger.critical("💀 FORCE-QUIT ABGESCHLOSSEN - SYSTEM WIRD BEENDET")
|
||||
|
||||
if self.show_terminal_countdown and sys.stdout.isatty():
|
||||
with self.terminal_lock:
|
||||
print("💀 FORCE-QUIT ABGESCHLOSSEN", flush=True)
|
||||
|
||||
# Kurze Verzögerung für Log-Ausgabe
|
||||
time.sleep(1)
|
||||
|
||||
# System beenden
|
||||
os._exit(1)
|
||||
|
||||
except Exception as e:
|
||||
logger.critical(f"❌ KRITISCHER FEHLER IM FORCE-QUIT: {e}")
|
||||
# Notfall-Exit
|
||||
os._exit(1)
|
||||
|
||||
def _perform_database_cleanup(self):
|
||||
"""Führt robuste Datenbankbereinigung durch"""
|
||||
try:
|
||||
logger.info("💾 Starte Datenbankbereinigung...")
|
||||
|
||||
if self.show_terminal_countdown and sys.stdout.isatty():
|
||||
with self.terminal_lock:
|
||||
print("\r💾 Datenbankbereinigung läuft..." + " " * 30, flush=True)
|
||||
|
||||
# 1. Verwende modernen DatabaseCleanupManager falls verfügbar
|
||||
if DATABASE_CLEANUP_AVAILABLE:
|
||||
logger.info("🔧 Verwende DatabaseCleanupManager...")
|
||||
result = safe_database_cleanup(
|
||||
force_mode_switch=True, # Aggressive Bereinigung
|
||||
max_cleanup_time=10 # 10 Sekunden Maximum
|
||||
)
|
||||
|
||||
if result.get("success", False):
|
||||
logger.info(f"✅ Database-Cleanup erfolgreich: {', '.join(result.get('operations', []))}")
|
||||
else:
|
||||
logger.warning(f"⚠️ Database-Cleanup mit Problemen: {', '.join(result.get('errors', []))}")
|
||||
# Fallback verwenden
|
||||
self._fallback_database_cleanup()
|
||||
else:
|
||||
# 2. Fallback: Direkter SQLite-Cleanup
|
||||
self._fallback_database_cleanup()
|
||||
|
||||
# 3. WAL/SHM-Dateien manuell bereinigen falls gewünscht
|
||||
if self.force_wal_cleanup:
|
||||
self._force_wal_shm_cleanup()
|
||||
|
||||
logger.info("✅ Datenbankbereinigung abgeschlossen")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler bei Datenbankbereinigung: {e}")
|
||||
# Versuche trotzdem WAL/SHM-Cleanup
|
||||
if self.force_wal_cleanup:
|
||||
try:
|
||||
self._force_wal_shm_cleanup()
|
||||
except:
|
||||
pass
|
||||
|
||||
def _fallback_database_cleanup(self):
|
||||
"""Fallback-Datenbankbereinigung mit direkten SQLite-Befehlen"""
|
||||
try:
|
||||
from models import create_optimized_engine
|
||||
from sqlalchemy import text
|
||||
|
||||
logger.info("🔄 Fallback Database-Cleanup...")
|
||||
|
||||
engine = create_optimized_engine()
|
||||
|
||||
with engine.connect() as conn:
|
||||
# WAL-Checkpoint (TRUNCATE für vollständige Bereinigung)
|
||||
result = conn.execute(text("PRAGMA wal_checkpoint(TRUNCATE)")).fetchone()
|
||||
if result and result[1] > 0:
|
||||
logger.info(f"WAL-Checkpoint: {result[1]} Seiten übertragen")
|
||||
|
||||
# Alle ausstehenden Transaktionen committen
|
||||
conn.commit()
|
||||
|
||||
# Verbindung optimieren
|
||||
conn.execute(text("PRAGMA optimize"))
|
||||
|
||||
logger.info("✅ Fallback Database-Cleanup abgeschlossen")
|
||||
|
||||
# Engine ordnungsgemäß schließen
|
||||
engine.dispose()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler im Fallback Database-Cleanup: {e}")
|
||||
|
||||
def _force_wal_shm_cleanup(self):
|
||||
"""Aggressive Bereinigung von WAL/SHM-Dateien"""
|
||||
try:
|
||||
from config.settings import DATABASE_PATH
|
||||
|
||||
logger.info("🧹 Force WAL/SHM-Cleanup...")
|
||||
|
||||
if self.show_terminal_countdown and sys.stdout.isatty():
|
||||
with self.terminal_lock:
|
||||
print("\r🧹 WAL/SHM-Dateien werden bereinigt..." + " " * 20, flush=True)
|
||||
|
||||
# Kurze Pause um sicherzustellen, dass alle DB-Verbindungen geschlossen sind
|
||||
time.sleep(0.5)
|
||||
|
||||
# WAL-Datei
|
||||
wal_path = DATABASE_PATH + "-wal"
|
||||
if os.path.exists(wal_path):
|
||||
try:
|
||||
# Versuche erst normales Löschen
|
||||
os.remove(wal_path)
|
||||
logger.info(f"✅ WAL-Datei gelöscht: {wal_path}")
|
||||
except OSError:
|
||||
# Falls blockiert, versuche Umbenennung und Löschung
|
||||
try:
|
||||
backup_path = wal_path + f".backup_{int(time.time())}"
|
||||
shutil.move(wal_path, backup_path)
|
||||
os.remove(backup_path)
|
||||
logger.info(f"✅ WAL-Datei über Backup gelöscht: {wal_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ WAL-Datei konnte nicht gelöscht werden: {e}")
|
||||
|
||||
# SHM-Datei
|
||||
shm_path = DATABASE_PATH + "-shm"
|
||||
if os.path.exists(shm_path):
|
||||
try:
|
||||
os.remove(shm_path)
|
||||
logger.info(f"✅ SHM-Datei gelöscht: {shm_path}")
|
||||
except OSError:
|
||||
try:
|
||||
backup_path = shm_path + f".backup_{int(time.time())}"
|
||||
shutil.move(shm_path, backup_path)
|
||||
os.remove(backup_path)
|
||||
logger.info(f"✅ SHM-Datei über Backup gelöscht: {shm_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ SHM-Datei konnte nicht gelöscht werden: {e}")
|
||||
|
||||
logger.info("✅ Force WAL/SHM-Cleanup abgeschlossen")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Fehler bei Force WAL/SHM-Cleanup: {e}")
|
||||
|
||||
def get_status(self) -> Dict[str, Any]:
|
||||
"""Gibt den aktuellen Status zurück"""
|
||||
if not self.is_active:
|
||||
return {
|
||||
"active": False,
|
||||
"remaining_seconds": 0,
|
||||
"progress_percent": 0.0
|
||||
}
|
||||
|
||||
elapsed = (datetime.now() - self.start_time).total_seconds()
|
||||
remaining = max(0, self.timeout_seconds - elapsed)
|
||||
progress = 1.0 - (remaining / self.timeout_seconds) if self.timeout_seconds > 0 else 1.0
|
||||
|
||||
return {
|
||||
"active": True,
|
||||
"remaining_seconds": int(remaining),
|
||||
"progress_percent": round(progress * 100, 1),
|
||||
"is_warning": remaining <= self.warning_seconds,
|
||||
"start_time": self.start_time.isoformat() if self.start_time else None
|
||||
}
|
||||
|
||||
|
||||
# ===== GLOBALER MANAGER UND UTILITY-FUNKTIONEN =====
|
||||
|
||||
_timeout_manager: Optional[TimeoutForceQuitManager] = None
|
||||
_manager_lock = threading.Lock()
|
||||
|
||||
|
||||
def get_timeout_manager(timeout_seconds: int = 45,
|
||||
warning_seconds: int = 15,
|
||||
database_cleanup: bool = True,
|
||||
force_wal_cleanup: bool = True) -> TimeoutForceQuitManager:
|
||||
"""
|
||||
Singleton-Pattern für globalen Timeout-Manager.
|
||||
|
||||
Args:
|
||||
timeout_seconds: Gesamttimeout in Sekunden
|
||||
warning_seconds: Warnzeit vor Force-Quit
|
||||
database_cleanup: Datenbankbereinigung aktivieren
|
||||
force_wal_cleanup: Aggressive WAL/SHM-Bereinigung
|
||||
|
||||
Returns:
|
||||
TimeoutForceQuitManager: Globaler Timeout-Manager
|
||||
"""
|
||||
global _timeout_manager
|
||||
|
||||
with _manager_lock:
|
||||
if _timeout_manager is None:
|
||||
_timeout_manager = TimeoutForceQuitManager(
|
||||
timeout_seconds=timeout_seconds,
|
||||
warning_seconds=warning_seconds,
|
||||
database_cleanup=database_cleanup,
|
||||
force_wal_cleanup=force_wal_cleanup
|
||||
)
|
||||
|
||||
return _timeout_manager
|
||||
|
||||
|
||||
def start_force_quit_timeout(reason: str = "System-Timeout",
|
||||
timeout_seconds: int = 45,
|
||||
warning_seconds: int = 15,
|
||||
database_cleanup: bool = True,
|
||||
force_wal_cleanup: bool = True) -> bool:
|
||||
"""
|
||||
Startet einen Force-Quit-Timeout mit Terminal-Countdown.
|
||||
|
||||
Args:
|
||||
reason: Grund für den Timeout
|
||||
timeout_seconds: Gesamttimeout in Sekunden
|
||||
warning_seconds: Warnzeit vor Force-Quit
|
||||
database_cleanup: Datenbankbereinigung aktivieren
|
||||
force_wal_cleanup: Aggressive WAL/SHM-Bereinigung
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich gestartet
|
||||
"""
|
||||
manager = get_timeout_manager(timeout_seconds, warning_seconds, database_cleanup, force_wal_cleanup)
|
||||
return manager.start_timeout(reason)
|
||||
|
||||
|
||||
def cancel_force_quit_timeout() -> bool:
|
||||
"""
|
||||
Bricht den aktuellen Force-Quit-Timeout ab.
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich abgebrochen
|
||||
"""
|
||||
global _timeout_manager
|
||||
|
||||
if _timeout_manager:
|
||||
return _timeout_manager.cancel_timeout()
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def extend_force_quit_timeout(additional_seconds: int) -> bool:
|
||||
"""
|
||||
Verlängert den aktuellen Force-Quit-Timeout.
|
||||
|
||||
Args:
|
||||
additional_seconds: Zusätzliche Sekunden
|
||||
|
||||
Returns:
|
||||
bool: True wenn erfolgreich verlängert
|
||||
"""
|
||||
global _timeout_manager
|
||||
|
||||
if _timeout_manager:
|
||||
return _timeout_manager.extend_timeout(additional_seconds)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_force_quit_status() -> Dict[str, Any]:
|
||||
"""
|
||||
Gibt den Status des aktuellen Force-Quit-Timeouts zurück.
|
||||
|
||||
Returns:
|
||||
Dict: Status-Informationen
|
||||
"""
|
||||
global _timeout_manager
|
||||
|
||||
if _timeout_manager:
|
||||
return _timeout_manager.get_status()
|
||||
|
||||
return {"active": False, "remaining_seconds": 0, "progress_percent": 0.0}
|
||||
|
||||
|
||||
@contextmanager
|
||||
def timeout_context(timeout_seconds: int = 45,
|
||||
reason: str = "Operation-Timeout",
|
||||
auto_cancel: bool = True):
|
||||
"""
|
||||
Context-Manager für automatischen Timeout-Schutz.
|
||||
|
||||
Args:
|
||||
timeout_seconds: Timeout in Sekunden
|
||||
reason: Grund für den Timeout
|
||||
auto_cancel: Automatisch abbrechen beim Verlassen des Contexts
|
||||
|
||||
Usage:
|
||||
with timeout_context(30, "Datenbank-Migration"):
|
||||
# Lange Operation...
|
||||
pass
|
||||
"""
|
||||
manager = get_timeout_manager(timeout_seconds)
|
||||
success = manager.start_timeout(reason)
|
||||
|
||||
try:
|
||||
yield manager
|
||||
finally:
|
||||
if success and auto_cancel:
|
||||
manager.cancel_timeout()
|
||||
|
||||
|
||||
def register_shutdown_callback(callback: Callable):
|
||||
"""
|
||||
Registriert eine Callback-Funktion für den Shutdown.
|
||||
|
||||
Args:
|
||||
callback: Callback-Funktion die beim Shutdown ausgeführt wird
|
||||
"""
|
||||
manager = get_timeout_manager()
|
||||
manager.set_shutdown_callback(callback)
|
||||
|
||||
|
||||
# ===== INTEGRATION MIT SHUTDOWN-MANAGER =====
|
||||
|
||||
def integrate_with_shutdown_manager():
|
||||
"""Integriert den Timeout-Manager mit dem bestehenden Shutdown-Manager"""
|
||||
try:
|
||||
from utils.shutdown_manager import get_shutdown_manager
|
||||
|
||||
shutdown_manager = get_shutdown_manager()
|
||||
|
||||
# Force-Quit-Timeout als Cleanup-Funktion registrieren
|
||||
def timeout_cleanup():
|
||||
global _timeout_manager
|
||||
if _timeout_manager and _timeout_manager.is_active:
|
||||
logger.info("🔄 Timeout-Manager wird im Shutdown-Prozess gestoppt")
|
||||
_timeout_manager.cancel_timeout()
|
||||
|
||||
shutdown_manager.register_cleanup_function(
|
||||
func=timeout_cleanup,
|
||||
name="Timeout Force-Quit Manager",
|
||||
priority=1, # Hohe Priorität
|
||||
timeout=5
|
||||
)
|
||||
|
||||
logger.debug("✅ Timeout-Manager in Shutdown-Manager integriert")
|
||||
|
||||
except ImportError:
|
||||
logger.debug("Shutdown-Manager nicht verfügbar - keine Integration")
|
||||
except Exception as e:
|
||||
logger.warning(f"Fehler bei Shutdown-Manager-Integration: {e}")
|
||||
|
||||
|
||||
# Automatische Integration beim Import
|
||||
integrate_with_shutdown_manager()
|
Reference in New Issue
Block a user