From f361e0e91782148aacde1e93f7d10ae8678973bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=80=AA=E7=A8=8B=E4=BC=9F?= Date: Sun, 14 Jun 2026 01:18:34 +0800 Subject: [PATCH] fix: reuse shared HttpClient to prevent thread-leak OOM on model test openAiCompatibleClientBuilder() was creating a new java.net.http.HttpClient per request. Each instance spawns a selector thread and connection pool that are never closed, exhausting the OS thread limit under frequent model-test calls (e.g. DeepSeek provider). Elevate the HttpClient to a static singleton so all OpenAI-compatible provider requests share one connection pool and one selector thread. Closes matevip/mateclaw#328 --- .../vip/mate/llm/service/ModelDiscoveryService.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/mateclaw-server/src/main/java/vip/mate/llm/service/ModelDiscoveryService.java b/mateclaw-server/src/main/java/vip/mate/llm/service/ModelDiscoveryService.java index 6866a4eb..df18b4ec 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/service/ModelDiscoveryService.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/service/ModelDiscoveryService.java @@ -66,6 +66,13 @@ public class ModelDiscoveryService { private static final Duration TIMEOUT = Duration.ofSeconds(10); + // Shared HttpClient for OpenAI-compatible providers — avoids creating a new + // native thread + connection pool per request (was causing thread-leak OOM). + private static final HttpClient SHARED_HTTP_CLIENT = HttpClient.newBuilder() + .version(HttpClient.Version.HTTP_1_1) + .connectTimeout(Duration.ofSeconds(30)) + .build(); + // Virtual-thread executor for parallel model probing (lightweight, short-lived) private static final ExecutorService PROBE_EXECUTOR = Executors.newVirtualThreadPerTaskExecutor(); @@ -962,10 +969,7 @@ public class ModelDiscoveryService { * the upgrade negotiation. */ private RestClient.Builder openAiCompatibleClientBuilder() { - HttpClient httpClient = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .build(); - return RestClient.builder().requestFactory(new JdkClientHttpRequestFactory(httpClient)); + return RestClient.builder().requestFactory(new JdkClientHttpRequestFactory(SHARED_HTTP_CLIENT)); } @SuppressWarnings("unchecked")