From 6774067505b4957052300f50e7751a60b87197ca Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 8 Jun 2026 03:17:34 +0900 Subject: [PATCH] =?UTF-8?q?fix(insta-render):=20=ED=81=90=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20socket=5Ftimeout=3D30=20(None=E2=86=9230=20?= =?UTF-8?q?=EA=B5=90=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 근본원인 실험 확정: redis-py 블로킹 read에서 socket_timeout이 BLMOVE 블록(5s) 이하/None이면 read_timeout 경계 경합으로 간헐 "Timeout reading" → dequeue 실패 → 슬레이트 draft 정지. socket_timeout 10/30은 모든 실험에서 안정. 블록보다 큰 30으로 명시(직전 None 커밋은 단독 테스트만 통과시켜 오도 — 재사용 패턴서 깨짐). Co-Authored-By: Claude Opus 4.8 (1M context) --- services/insta-render/tests/test_worker.py | 13 +++++-------- services/insta-render/worker.py | 17 +++++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/services/insta-render/tests/test_worker.py b/services/insta-render/tests/test_worker.py index 462981f..ef47b47 100644 --- a/services/insta-render/tests/test_worker.py +++ b/services/insta-render/tests/test_worker.py @@ -224,12 +224,9 @@ async def test_poll_once_returns_false_on_timeout(monkeypatch): fake_queue.fail.assert_not_awaited() -def test_make_queue_redis_no_read_timeout(): - """BLMOVE(블록 5s) dequeue가 read-timeout으로 깨지지 않도록 socket_timeout=None 보장 (회귀 가드).""" - import os, sys - _here = os.path.dirname(os.path.abspath(__file__)) - sys.path.insert(0, os.path.dirname(_here)) # services/insta-render - sys.path.insert(0, os.path.dirname(os.path.dirname(_here))) # services (_shared) - import worker +def test_make_queue_redis_socket_timeout_exceeds_block(): + """BLMOVE(블록 5s) dequeue가 read-timeout 경계 경합으로 깨지지 않도록 + socket_timeout이 블록보다 충분히 커야 한다 (회귀 가드).""" c = worker.make_queue_redis() - assert c.connection_pool.connection_kwargs.get("socket_timeout") is None + st = c.connection_pool.connection_kwargs.get("socket_timeout") + assert st is not None and st > 5 # blmove 블록(5s)보다 커야 안정 diff --git a/services/insta-render/worker.py b/services/insta-render/worker.py index 9564640..86dd207 100644 --- a/services/insta-render/worker.py +++ b/services/insta-render/worker.py @@ -98,17 +98,18 @@ async def poll_once(queue: ReliableQueue, client: httpx.AsyncClient) -> bool: return True -def make_queue_redis(): - """블로킹 dequeue(BLMOVE 5s)용 redis 클라이언트. +# 블로킹 dequeue는 BLMOVE(블록 5s)를 쓴다. redis-py 블로킹 read에서 socket_timeout이 +# 블록(5s) 이하이거나 None이면 read-timeout이 블록 경계와 경합해 간헐적으로 +# "Timeout reading"이 터져 잡을 못 꺼낸다(슬레이트 draft 정지). 실험상 socket_timeout이 +# 블록보다 충분히 크면(10/30) 항상 안정. → 블록보다 넉넉히 큰 값을 명시한다. +QUEUE_SOCKET_TIMEOUT = 30 # > dequeue blmove 블록(5s) - BLMOVE 블록보다 짧은 socket_timeout(예: REDIS_URL ?socket_timeout=)이 걸려 있으면 - idle 폴링마다 "Timeout reading"으로 dequeue가 실패해 잡을 영영 못 꺼낸다(슬레이트 draft 정지). - → read-timeout을 두지 않는다(socket_timeout=None). 죽은 연결은 socket_keepalive + - worker_loop 재시도로 감지/복구. (explicit kwarg가 URL의 socket_timeout을 override) - """ + +def make_queue_redis(): + """블로킹 dequeue(BLMOVE)용 redis 클라이언트. socket_timeout > 블록(5s) 보장.""" return aioredis.from_url( REDIS_URL, decode_responses=False, - socket_timeout=None, socket_keepalive=True, + socket_timeout=QUEUE_SOCKET_TIMEOUT, socket_keepalive=True, )