Core Systems:

Component-based architecture with lifecycle management
Enhanced error handling and recovery mechanisms
Comprehensive state management and tracking
Event-driven architecture with monitoring
Queue Management:

Multiple processing strategies for different scenarios
Advanced state management with recovery
Comprehensive metrics and health monitoring
Sophisticated cleanup system with multiple strategies
Processing Pipeline:

Enhanced message handling with validation
Improved URL extraction and processing
Better queue management and monitoring
Advanced cleanup mechanisms
Overall Benefits:

Better code organization and maintainability
Improved error handling and recovery
Enhanced monitoring and reporting
More robust and reliable system
This commit is contained in:
pacnpal
2024-11-16 05:01:29 +00:00
parent 537a325807
commit a4ca6e8ea6
47 changed files with 11085 additions and 2110 deletions

View File

@@ -0,0 +1,239 @@
"""Module for managing VideoArchiver lifecycle"""
import asyncio
import logging
from typing import Optional, Dict, Any, Set
from enum import Enum
from datetime import datetime
from .cleanup import cleanup_resources, force_cleanup_resources
from ..utils.exceptions import VideoArchiverError
from .initialization import initialize_cog, init_callback
logger = logging.getLogger("VideoArchiver")
class LifecycleState(Enum):
"""Possible states in the cog lifecycle"""
UNINITIALIZED = "uninitialized"
INITIALIZING = "initializing"
READY = "ready"
UNLOADING = "unloading"
ERROR = "error"
class TaskManager:
"""Manages asyncio tasks"""
def __init__(self):
self._tasks: Dict[str, asyncio.Task] = {}
self._task_history: Dict[str, Dict[str, Any]] = {}
async def create_task(
self,
name: str,
coro,
callback=None
) -> asyncio.Task:
"""Create and track a task"""
task = asyncio.create_task(coro)
self._tasks[name] = task
self._task_history[name] = {
"start_time": datetime.utcnow(),
"status": "running"
}
if callback:
task.add_done_callback(lambda t: self._handle_completion(name, t, callback))
else:
task.add_done_callback(lambda t: self._handle_completion(name, t))
return task
def _handle_completion(
self,
name: str,
task: asyncio.Task,
callback=None
) -> None:
"""Handle task completion"""
try:
task.result() # Raises exception if task failed
status = "completed"
except asyncio.CancelledError:
status = "cancelled"
except Exception as e:
status = "failed"
logger.error(f"Task {name} failed: {e}")
self._task_history[name].update({
"end_time": datetime.utcnow(),
"status": status
})
if callback:
try:
callback(task)
except Exception as e:
logger.error(f"Task callback error for {name}: {e}")
self._tasks.pop(name, None)
async def cancel_task(self, name: str) -> None:
"""Cancel a specific task"""
if task := self._tasks.get(name):
if not task.done():
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
except Exception as e:
logger.error(f"Error cancelling task {name}: {e}")
async def cancel_all_tasks(self) -> None:
"""Cancel all tracked tasks"""
for name in list(self._tasks.keys()):
await self.cancel_task(name)
def get_task_status(self) -> Dict[str, Any]:
"""Get status of all tasks"""
return {
"active_tasks": list(self._tasks.keys()),
"history": self._task_history.copy()
}
class StateTracker:
"""Tracks lifecycle state and transitions"""
def __init__(self):
self.state = LifecycleState.UNINITIALIZED
self.state_history: List[Dict[str, Any]] = []
self._record_state()
def set_state(self, state: LifecycleState) -> None:
"""Set current state"""
self.state = state
self._record_state()
def _record_state(self) -> None:
"""Record state transition"""
self.state_history.append({
"state": self.state.value,
"timestamp": datetime.utcnow()
})
def get_state_history(self) -> List[Dict[str, Any]]:
"""Get state transition history"""
return self.state_history.copy()
class LifecycleManager:
"""Manages the lifecycle of the VideoArchiver cog"""
def __init__(self, cog):
self.cog = cog
self.task_manager = TaskManager()
self.state_tracker = StateTracker()
self._cleanup_handlers: Set[callable] = set()
def register_cleanup_handler(self, handler: callable) -> None:
"""Register a cleanup handler"""
self._cleanup_handlers.add(handler)
async def handle_load(self) -> None:
"""Handle cog loading without blocking"""
try:
self.state_tracker.set_state(LifecycleState.INITIALIZING)
# Start initialization as background task
await self.task_manager.create_task(
"initialization",
initialize_cog(self.cog),
lambda t: init_callback(self.cog, t)
)
logger.info("Initialization started in background")
except Exception as e:
self.state_tracker.set_state(LifecycleState.ERROR)
# Ensure cleanup on any error
try:
await asyncio.wait_for(
force_cleanup_resources(self.cog),
timeout=15 # CLEANUP_TIMEOUT
)
except asyncio.TimeoutError:
logger.error("Force cleanup during load error timed out")
raise VideoArchiverError(f"Error during cog load: {str(e)}")
async def handle_unload(self) -> None:
"""Clean up when cog is unloaded"""
self.state_tracker.set_state(LifecycleState.UNLOADING)
try:
# Cancel all tasks
await self.task_manager.cancel_all_tasks()
# Run cleanup handlers
await self._run_cleanup_handlers()
# Try normal cleanup
try:
cleanup_task = await self.task_manager.create_task(
"cleanup",
cleanup_resources(self.cog)
)
await asyncio.wait_for(cleanup_task, timeout=30) # UNLOAD_TIMEOUT
logger.info("Normal cleanup completed")
except (asyncio.TimeoutError, Exception) as e:
if isinstance(e, asyncio.TimeoutError):
logger.warning("Normal cleanup timed out, forcing cleanup")
else:
logger.error(f"Error during normal cleanup: {str(e)}")
# Force cleanup
try:
await asyncio.wait_for(
force_cleanup_resources(self.cog),
timeout=15 # CLEANUP_TIMEOUT
)
logger.info("Force cleanup completed")
except asyncio.TimeoutError:
logger.error("Force cleanup timed out")
except Exception as e:
logger.error(f"Error during force cleanup: {str(e)}")
except Exception as e:
logger.error(f"Error during cog unload: {str(e)}")
self.state_tracker.set_state(LifecycleState.ERROR)
finally:
# Clear all references
await self._cleanup_references()
async def _run_cleanup_handlers(self) -> None:
"""Run all registered cleanup handlers"""
for handler in self._cleanup_handlers:
try:
if asyncio.iscoroutinefunction(handler):
await handler()
else:
handler()
except Exception as e:
logger.error(f"Error in cleanup handler: {e}")
async def _cleanup_references(self) -> None:
"""Clean up all references"""
self.cog.ready.clear()
self.cog.bot = None
self.cog.processor = None
self.cog.queue_manager = None
self.cog.update_checker = None
self.cog.ffmpeg_mgr = None
self.cog.components.clear()
self.cog.db = None
def get_status(self) -> Dict[str, Any]:
"""Get current lifecycle status"""
return {
"state": self.state_tracker.state.value,
"state_history": self.state_tracker.get_state_history(),
"tasks": self.task_manager.get_task_status()
}