|
17 | 17 |
|
18 | 18 | from tux.core.cog_loader import CogLoader |
19 | 19 | from tux.core.container import ServiceContainer |
| 20 | +from tux.core.prefix_manager import PrefixManager |
20 | 21 | from tux.core.service_registry import ServiceRegistry |
21 | 22 | from tux.core.task_monitor import TaskMonitor |
22 | 23 | from tux.database.migrations.runner import upgrade_head_if_needed |
@@ -90,6 +91,8 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: |
90 | 91 | self.container: ServiceContainer | None = None |
91 | 92 | # Sentry manager instance for error handling and context utilities |
92 | 93 | self.sentry_manager: SentryManager = SentryManager() |
| 94 | + # Prefix manager for efficient prefix resolution |
| 95 | + self.prefix_manager: Any | None = None |
93 | 96 |
|
94 | 97 | # UI / misc |
95 | 98 | self.emoji_manager = EmojiManager(self) |
@@ -135,6 +138,8 @@ async def setup(self) -> None: # noqa: PLR0912, PLR0915 |
135 | 138 | raise DatabaseConnectionError(db_migration_error) from e |
136 | 139 | raise |
137 | 140 | set_setup_phase_tag(span, "database", "finished") |
| 141 | + await self._setup_prefix_manager() |
| 142 | + set_setup_phase_tag(span, "prefix_manager", "finished") |
138 | 143 | await self._load_drop_in_extensions() |
139 | 144 | set_setup_phase_tag(span, "extensions", "finished") |
140 | 145 | await self._load_cogs() |
@@ -293,6 +298,38 @@ def _raise_container_error(message: str) -> None: |
293 | 298 | error_msg = f"Container initialization failed: {e}" |
294 | 299 | raise ContainerInitializationError(error_msg) from e |
295 | 300 |
|
| 301 | + async def _setup_prefix_manager(self) -> None: |
| 302 | + """Set up the prefix manager for efficient prefix resolution.""" |
| 303 | + with start_span("bot.setup_prefix_manager", "Setting up prefix manager") as span: |
| 304 | + logger.info("🔧 Initializing prefix manager...") |
| 305 | + |
| 306 | + try: |
| 307 | + # Initialize the prefix manager |
| 308 | + self.prefix_manager = PrefixManager(self) |
| 309 | + |
| 310 | + # Load all existing prefixes into cache with timeout |
| 311 | + await asyncio.wait_for( |
| 312 | + self.prefix_manager.load_all_prefixes(), |
| 313 | + timeout=15.0, # 15 second timeout for the entire setup |
| 314 | + ) |
| 315 | + |
| 316 | + span.set_tag("prefix_manager.initialized", True) |
| 317 | + logger.info("✅ Prefix manager initialized successfully") |
| 318 | + |
| 319 | + except TimeoutError: |
| 320 | + logger.warning("⚠️ Prefix manager setup timed out - continuing without cache") |
| 321 | + span.set_tag("prefix_manager.initialized", False) |
| 322 | + span.set_data("error", "timeout") |
| 323 | + self.prefix_manager = None |
| 324 | + except Exception as e: |
| 325 | + logger.error(f"❌ Failed to initialize prefix manager: {type(e).__name__}: {e}") |
| 326 | + span.set_tag("prefix_manager.initialized", False) |
| 327 | + span.set_data("error", str(e)) |
| 328 | + |
| 329 | + # Don't fail startup if prefix manager fails - bot can still work with default prefix |
| 330 | + logger.warning("⚠️ Bot will use default prefix for all guilds") |
| 331 | + self.prefix_manager = None |
| 332 | + |
296 | 333 | async def _load_drop_in_extensions(self) -> None: |
297 | 334 | """Load optional drop-in extensions (e.g., Jishaku).""" |
298 | 335 | with start_span("bot.load_drop_in_extensions", "Loading drop-in extensions") as span: |
@@ -393,6 +430,18 @@ async def _post_ready_startup(self) -> None: |
393 | 430 |
|
394 | 431 | self._record_bot_stats() |
395 | 432 |
|
| 433 | + def get_prefix_cache_stats(self) -> dict[str, int]: |
| 434 | + """Get prefix cache statistics for monitoring. |
| 435 | +
|
| 436 | + Returns |
| 437 | + ------- |
| 438 | + dict[str, int] |
| 439 | + Prefix cache statistics |
| 440 | + """ |
| 441 | + if self.prefix_manager: |
| 442 | + return self.prefix_manager.get_cache_stats() |
| 443 | + return {"cached_prefixes": 0, "cache_loaded": 0, "default_prefix": 0} |
| 444 | + |
396 | 445 | def _record_bot_stats(self) -> None: |
397 | 446 | """Record basic bot stats to Sentry context (if available).""" |
398 | 447 | if not self.sentry_manager.is_initialized: |
|
0 commit comments