fix(news): 연결 계층(TCP/TLS) 오류 1회 재시도 — MOEL 보안장비 첫 핸드셰이크 간헐 드랍 (재실측 진단)

GPU 회선에서 moel.go.kr 첫 TLS 연결이 간헐 드랍(curl rc=35, 직후 재시도 5/5 성공,
맥북 무발생·단일 A 레코드) → 사이클당 1회 fetch 인 피드가 ConnectError('') 누적,
입법행정예고 circuit open. ConnectError/ConnectTimeout 만 1.5s 후 1회 재시도,
HTTP 상태 오류 비대상. 회귀 테스트 3건 (42 passed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
hyungi
2026-06-11 07:43:05 +09:00
parent f3530e382d
commit b75307b89b
2 changed files with 54 additions and 1 deletions
+38
View File
@@ -117,6 +117,44 @@ class TestSignalOnlyEnqueueGuard:
assert calls == ["embed", "chunk"]
# ── 연결 계층 1회 재시도 (MOEL 첫 TLS 핸드셰이크 간헐 드랍 실측) ──────────────
class TestConnectRetry:
class _Client:
def __init__(self, errors: list):
self.errors = errors
self.calls = 0
async def get(self, url):
self.calls += 1
if self.errors:
raise self.errors.pop(0)
return "OK"
@pytest.mark.asyncio
async def test_single_connect_error_retried_once(self):
import httpx
client = self._Client([httpx.ConnectError("")])
resp = await news_collector._get_with_connect_retry(client, "https://x/feed")
assert resp == "OK" and client.calls == 2
@pytest.mark.asyncio
async def test_persistent_connect_error_propagates(self):
import httpx
client = self._Client([httpx.ConnectError(""), httpx.ConnectError("")])
with pytest.raises(httpx.ConnectError):
await news_collector._get_with_connect_retry(client, "https://x/feed")
assert client.calls == 2 # 1회만 재시도 — 지속 장애는 circuit 몫
@pytest.mark.asyncio
async def test_non_connect_errors_not_retried(self):
import httpx
client = self._Client([httpx.ReadTimeout("")])
with pytest.raises(httpx.ReadTimeout):
await news_collector._get_with_connect_retry(client, "https://x/feed")
assert client.calls == 1
# ── C-4 / B-4 피드 shape (시드 전 live 박제) ─────────────────────────────────
class TestNikkeiRdfNativeParsing: