From 40e448fe952296546a23226d0f883defba4c4df6 Mon Sep 17 00:00:00 2001 From: joungmin Date: Mon, 15 Jun 2026 21:52:31 +0900 Subject: [PATCH] =?UTF-8?q?fix(search):=20WebSearchService=20HTTP=20timeou?= =?UTF-8?q?t=20=EC=B6=94=EA=B0=80=20(connect=205s,=20request=2015s)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 특정 검색에서 무한 hang → backend virtual thread 점유로 후속 벌크 작업 중단 - Naver/DDG 둘 다 timeout 적용 - 타임아웃 시 HttpTimeoutException → 호출자(bulk)에서 notfound 안전 처리 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 --- CHANGELOG.md | 5 +++++ .../src/main/java/com/tasteby/service/WebSearchService.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d9fd2e..f5c89d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ ## 2026-06-15 +### 🐛 WebSearchService HTTP timeout 추가 (v0.1.49) +- 벌크 백필 중 특정 검색에서 무한 hang → backend executor virtual thread 점유로 후속 작업 중단 (90건 처리 후 멈춤) +- connectTimeout=5s + request timeout=15s (Naver/DDG 둘 다) +- 해당 식당은 HttpTimeoutException → notfound로 안전 처리 + ### ⏱️ bulk-tabling/catchtable SSE timeout 10분 → 3시간 (v0.1.48) - 대량 백필(724건 ≈ 100분) 시 10분 SSE timeout으로 중간 끊김 → 3시간으로 확장 - 백엔드 작업은 virtual thread로 별도 진행됐지만 emit() 예외로 마지막 cache.flush + complete 누락이슈 해소 diff --git a/backend-java/src/main/java/com/tasteby/service/WebSearchService.java b/backend-java/src/main/java/com/tasteby/service/WebSearchService.java index dfb178d..1d3e43f 100644 --- a/backend-java/src/main/java/com/tasteby/service/WebSearchService.java +++ b/backend-java/src/main/java/com/tasteby/service/WebSearchService.java @@ -14,6 +14,7 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,8 +31,10 @@ public class WebSearchService { private static final Logger log = LoggerFactory.getLogger(WebSearchService.class); private static final int MAX_RESULTS = 5; + private static final Duration REQ_TIMEOUT = Duration.ofSeconds(15); private static final HttpClient HTTP = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.NORMAL) + .connectTimeout(Duration.ofSeconds(5)) .build(); private static final Pattern DDG_RESULT = Pattern.compile( @@ -74,6 +77,7 @@ public class WebSearchService { String url = "https://openapi.naver.com/v1/search/webkr.json?query=" + encoded + "&display=30"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create(url)) + .timeout(REQ_TIMEOUT) .header("X-Naver-Client-Id", naverClientId) .header("X-Naver-Client-Secret", naverClientSecret) .GET() @@ -104,6 +108,7 @@ public class WebSearchService { String url = "https://html.duckduckgo.com/html/?q=" + encoded; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create(url)) + .timeout(REQ_TIMEOUT) .header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36") .header("Accept", "text/html,application/xhtml+xml") .header("Accept-Language", "ko-KR,ko;q=0.9")