From 8556b1f5083695f6d7a7f465d81d17e96c6d94be Mon Sep 17 00:00:00 2001 From: gitadmin Date: Sat, 10 Jan 2026 14:54:31 +0300 Subject: [PATCH] =?UTF-8?q?[=D0=92=D0=9E=D0=9B=D0=9A]=20=D0=BE=D0=BF=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=B8=D0=B7=D0=B8=D1=80=D1=83=D0=B5=D0=BC=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20=D1=81=20event=5Floop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db_edit.py | 24 ++++++++++++++++++++++-- tg_publish.py | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/db_edit.py b/db_edit.py index 9d77077..b753502 100644 --- a/db_edit.py +++ b/db_edit.py @@ -1032,11 +1032,31 @@ def api_publish_post(post_id): # Функция для запуска в отдельном потоке def run_async_func(): try: + # Создаем новый event loop для этого потока + # Это важно при работе с gunicorn, который может иметь свой event loop loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) - result = loop.run_until_complete(publish_to_tg(post['vk_post_id'])) - return result + try: + result = loop.run_until_complete(publish_to_tg(post['vk_post_id'])) + return result + finally: + # Закрываем event loop после завершения + # Это важно для предотвращения утечек ресурсов + try: + # Отменяем все незавершенные задачи + pending = asyncio.all_tasks(loop) + for task in pending: + task.cancel() + # Ждем завершения отмененных задач + if pending: + loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True)) + except Exception: + pass + finally: + loop.close() except Exception as e: + import traceback + log_event(f"Ошибка в run_async_func: {str(e)}\n{traceback.format_exc()}") return e # Создаем очередь для передачи результата diff --git a/tg_publish.py b/tg_publish.py index d42d21e..491917d 100644 --- a/tg_publish.py +++ b/tg_publish.py @@ -135,9 +135,16 @@ async def publish_to_tg(vk_post_id): return False # Создаем httpx клиент с отключенным HTTP/2 + # Используем более детальные таймауты: connect, read, write, pool + timeout_config = httpx.Timeout( + connect=10.0, # Таймаут на подключение + read=60.0, # Таймаут на чтение (увеличен для больших изображений) + write=30.0, # Таймаут на запись + pool=10.0 # Таймаут на получение соединения из пула + ) httpx_client = httpx.AsyncClient( http2=False, - timeout=20.0, + timeout=timeout_config, follow_redirects=True ) @@ -199,6 +206,12 @@ async def publish_to_tg(vk_post_id): if has_image and not is_org_message: try: + # Логируем информацию об изображении перед отправкой + logger.info(f"Отправка изображения для записи VK ID {vk_post_id}") + logger.info(f"URL изображения: {image_url}") + logger.info(f"Длина URL изображения: {len(image_url)} символов") + logger.info(f"Длина подписи: {len(text)} символов") + photo_payload = { 'chat_id': CHANNEL_ID, 'photo': image_url, @@ -206,15 +219,29 @@ async def publish_to_tg(vk_post_id): 'parse_mode': 'HTML', 'disable_notification': PUBLISH_SILENTLY } + + # Логируем время начала запроса + request_start_time = time.time() + logger.info(f"Начало запроса отправки фото в {datetime.now(timezone.utc).isoformat()}") + try: photo_response = await httpx_client.post(send_photo_url, json=photo_payload) + request_duration = time.time() - request_start_time + logger.info(f"Запрос отправки фото завершен за {request_duration:.2f} секунд") except httpx.TimeoutException as e: + request_duration = time.time() - request_start_time logger.error(f"ТАЙМАУТ при отправке фото для записи VK ID {vk_post_id}") + logger.error(f"Время до таймаута: {request_duration:.2f} секунд") logger.error(f"Тип исключения: {type(e).__name__}") logger.error(f"Сообщение об ошибке: {str(e)}") logger.error(f"Полная информация об исключении: {repr(e)}") + logger.error(f"URL изображения: {image_url}") + logger.error(f"Длина URL изображения: {len(image_url)} символов") if hasattr(e, 'request'): logger.error(f"Запрос, вызвавший таймаут: {e.request.method} {e.request.url if hasattr(e.request, 'url') else 'N/A'}") + # Пытаемся получить информацию о таймауте из исключения + if hasattr(e, 'timeout'): + logger.error(f"Настройки таймаута: {e.timeout}") logger.error(f"Трассировка стека:\n{traceback.format_exc()}") raise # Пробрасываем таймаут наверх @@ -599,6 +626,13 @@ async def publish_to_tg(vk_post_id): logger.error(f"Полная информация об исключении: {repr(e)}") if hasattr(e, 'request'): logger.error(f"Запрос, вызвавший таймаут: {e.request.method} {e.request.url if hasattr(e.request, 'url') else 'N/A'}") + # Пытаемся получить информацию о таймауте из исключения + if hasattr(e, 'timeout'): + logger.error(f"Настройки таймаута: {e.timeout}") + # Проверяем, запущены ли мы из gunicorn + import sys + if 'gunicorn' in sys.modules: + logger.warning("Обнаружен gunicorn - возможен конфликт с event loop при запуске асинхронного кода в отдельном потоке") logger.error(f"Трассировка стека:\n{traceback.format_exc()}") # Убеждаемся, что БД не обновляется при таймауте return False