[ВОЛК] очередная отладка таймаутов
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-01-10 18:13:25 +03:00
parent 8087908c27
commit 538213eae9
2 changed files with 65 additions and 11 deletions
+63 -9
View File
@@ -8,6 +8,9 @@ import json
import re
import httpx
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from urllib3 import Timeout as Urllib3Timeout
import traceback
import io
import sys
@@ -153,11 +156,14 @@ def _send_photo_sync(send_photo_url, image_data, caption, chat_id, disable_notif
caption: Подпись к фото
chat_id: ID чата/канала
disable_notification: Отключить уведомления
timeout: Кортеж (connect_timeout, read_timeout) или число для общего таймаута
timeout: Кортеж (connect_timeout, read_timeout, write_timeout) или число для общего таймаута
Returns:
requests.Response объект
"""
import socket
from urllib3.connection import HTTPSConnection, HTTPConnection
try:
# Используем requests для стабильной работы в gunicorn
# requests более надежен для синхронных операций в контексте WSGI
@@ -172,17 +178,59 @@ def _send_photo_sync(send_photo_url, image_data, caption, chat_id, disable_notif
'disable_notification': str(disable_notification).lower()
}
# Извлекаем таймауты из кортежа
if isinstance(timeout, tuple):
if len(timeout) >= 3:
connect_timeout, read_timeout, write_timeout = timeout[0], timeout[1], timeout[2]
elif len(timeout) == 2:
connect_timeout, read_timeout = timeout[0], timeout[1]
write_timeout = read_timeout # Используем read timeout для write
else:
connect_timeout = read_timeout = write_timeout = timeout[0]
else:
connect_timeout = read_timeout = write_timeout = timeout
# Создаем сессию с кастомным адаптером
session = requests.Session()
# Создаем кастомный HTTPAdapter с установкой socket timeout
class CustomHTTPAdapter(HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
# Устанавливаем socket timeout через socket_options
# Это применяется ко всем операциям с сокетом (read и write)
socket_options = kwargs.get('socket_options', [])
socket_options.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1))
kwargs['socket_options'] = socket_options
pool = super().init_poolmanager(*args, **kwargs)
# Устанавливаем socket timeout для всех соединений в пуле
# Это применяется ко всем операциям с сокетом (read и write)
original_connect = pool.ConnectionCls.connect
def connect_with_timeout(self):
result = original_connect(self)
if hasattr(self, 'sock') and self.sock:
self.sock.settimeout(write_timeout)
return result
pool.ConnectionCls.connect = connect_with_timeout
return pool
# Монтируем кастомный адаптер
adapter = CustomHTTPAdapter(max_retries=Retry(total=0))
session.mount('http://', adapter)
session.mount('https://', adapter)
# Отправляем запрос с увеличенным таймаутом
# requests.post принимает timeout как кортеж (connect, read) или число
response = requests.post(
# Используем максимальный таймаут для всех операций
max_timeout = max(connect_timeout, read_timeout, write_timeout)
response = session.post(
send_photo_url,
files=files,
data=data,
timeout=timeout,
timeout=(connect_timeout, max_timeout), # (connect, read)
stream=False # Отключаем потоковую передачу для стабильности
)
return response
except requests.exceptions.Timeout as e:
# Пробрасываем таймаут как есть, чтобы его можно было обработать выше
raise
@@ -192,6 +240,10 @@ def _send_photo_sync(send_photo_url, image_data, caption, chat_id, disable_notif
except Exception as e:
# Пробрасываем остальные исключения как есть
raise
finally:
# Закрываем сессию
if 'session' in locals():
session.close()
def _edit_caption_sync(edit_caption_url, chat_id, message_id, caption, timeout):
"""
@@ -404,9 +456,9 @@ async def publish_to_tg(vk_post_id):
# Вызываем синхронную функцию через asyncio.to_thread()
# Это позволяет избежать проблем с event loop в gunicorn
try:
# Используем кортеж для таймаута: (connect, read)
# requests использует один таймаут для всех операций, поэтому используем максимальный
timeout_tuple = (10.0, write_timeout) # (connect, read/write)
# Используем кортеж для таймаута: (connect, read, write)
# write_timeout применяется к операциям записи в сокет
timeout_tuple = (10.0, write_timeout, write_timeout) # (connect, read, write)
photo_response = await asyncio.to_thread(
_send_photo_sync,
@@ -631,7 +683,8 @@ async def publish_to_tg(vk_post_id):
edit_caption_success = False
try:
# Используем requests через asyncio.to_thread() для стабильной работы в gunicorn
edit_timeout = (10.0, 30.0) # (connect, read)
# Увеличиваем таймаут для редактирования caption
edit_timeout = (10.0, 60.0, 60.0) # (connect, read, write)
edit_caption_response = await asyncio.to_thread(
_edit_caption_sync,
@@ -697,7 +750,8 @@ async def publish_to_tg(vk_post_id):
edit_text_success = False
try:
# Используем requests через asyncio.to_thread() для стабильной работы в gunicorn
edit_timeout = (10.0, 30.0) # (connect, read)
# Увеличиваем таймаут для редактирования текста
edit_timeout = (10.0, 60.0, 60.0) # (connect, read, write)
edit_text_response = await asyncio.to_thread(
_edit_text_sync,