4.0 KiB
4.0 KiB
Отчет об исправлении критической ошибки AsyncIO Event Loop
Проблема
При запуске бота в продакшене обнаружилась критическая ошибка:
RuntimeError: Task <Task pending> got Future <Future pending> attached to a different loop
Эта ошибка массово возникала у всех воркеров TaskManager (worker-0 до worker-14) и делала невозможным стабильную работу бота.
Причина
Проблема заключалась в том, что объекты asyncio.PriorityQueue() и asyncio.Semaphore() создавались в момент инициализации класса AsyncTaskManager, когда event loop еще не был запущен или был другой. Когда позже эти объекты использовались в основном event loop aiogram'а, возникал конфликт.
Проблемный код (до исправления):
def __init__(self, max_workers: int = 10, max_user_concurrent: int = 3):
self.max_workers = max_workers
self.max_user_concurrent = max_user_concurrent
# ❌ ПРОБЛЕМА: создаём asyncio объекты в неправильном event loop
self.task_queue = asyncio.PriorityQueue()
self.worker_semaphore = asyncio.Semaphore(max_workers)
self.user_semaphores: Dict[int, asyncio.Semaphore] = {}
Решение
Перенесли создание всех asyncio объектов в метод start(), который вызывается в правильном event loop:
Исправленный код:
def __init__(self, max_workers: int = 10, max_user_concurrent: int = 3):
self.max_workers = max_workers
self.max_user_concurrent = max_user_concurrent
# ✅ ИСПРАВЛЕНО: объекты будут созданы при запуске
self.task_queue: Optional[asyncio.PriorityQueue] = None
self.worker_semaphore: Optional[asyncio.Semaphore] = None
self.user_semaphores: Dict[int, asyncio.Semaphore] = {}
async def start(self):
"""Запуск менеджера задач"""
if self.running:
return
# ✅ Создаём asyncio объекты в правильном event loop
self.task_queue = asyncio.PriorityQueue()
self.worker_semaphore = asyncio.Semaphore(self.max_workers)
self.user_semaphores.clear() # Очищаем старые семафоры
self.running = True
# ... остальная логика
Дополнительные исправления
- Проверки на None: Добавили проверки во все методы, чтобы убедиться, что asyncio объекты созданы
- Правильная очистка: В методе
stop()очищаем все asyncio объекты - Безопасное создание семафоров пользователей: Семафоры пользователей теперь также создаются в правильном event loop
Результат
- ✅ Бот запускается без ошибок
- ✅ Все 15 воркеров TaskManager работают корректно
- ✅ Никаких RuntimeError с event loop
- ✅ Корректное завершение работы по Ctrl+C
Файлы изменены
task_manager.py- основные исправления AsyncTaskManager
Тестирование
Создан тест, подтверждающий корректную работу TaskManager:
# Тест показал успешное выполнение:
# Статистика: {'running': True, 'workers_count': 15, 'active_tasks': 0,
# 'queue_size': 0, 'completed_tasks': 1, 'failed_tasks': 0}
Бот успешно запускается и работает стабильно в продакшене.