Connection and Resource Management#
RAGFlow's multi-worker, long-running architecture creates three distinct resource exhaustion risks: file descriptor leaks from unclosed handles, HTTP connection starvation from misconfigured pools, and connection storms from concurrent worker initialization. Each failure mode has a distinct fix location and pattern.
HTTP Client: Per-Request Scoping#
common/http_client.py is the central HTTP wrapper for outbound requests. Both async_request and sync_request open an httpx.AsyncClient / httpx.Client inside a with block, so the transport (and all its connections) is created and destroyed within a single request's scope . This avoids connection accumulation at the cost of no long-lived pool — acceptable for one-off LLM and OAuth calls where call rates are moderate.
Key env-tunable defaults :
| Variable | Default | Purpose |
|---|---|---|
HTTP_CLIENT_TIMEOUT | 15s | Request timeout |
HTTP_CLIENT_MAX_RETRIES | 2 | Retry attempts |
HTTP_CLIENT_BACKOFF_FACTOR | 0.5 | Exponential backoff base |
HTTP_CLIENT_PROXY | — | Optional proxy for all requests |
Retries use exponential backoff: backoff_factor × 2^attempt .
Document Store Connection Pools#
OpenSearch#
The OSConnection singleton initializes a shared OpenSearch client. Prior to PR #15682, no pool_maxsize was set, causing urllib3 to default to maxsize=1 — forcing all concurrent operations to serialize on a single TCP connection and re-perform TLS handshakes. The fix passes pool_maxsize=10, enabling real parallel connections on the shared client.
Infinity#
common/doc_store/infinity_conn_pool.py controls the Infinity ConnectionPool via the INFINITY_POOL_MAX_SIZE environment variable (default: 4) . The default was reduced from 32 in PR #12006 after production deployments with 16 workers caused a connection storm: 16 workers × 32 connections = 512 simultaneous connections attempted at startup, exceeding Infinity's 128-connection limit and hanging most workers.
Multi-Worker Startup: Staggered Initialization#
To prevent the connection storm pattern from recurring, rag/svr/task_executor.py introduces a per-worker startup delay :
startup_delay = worker_num × 2.0s + random jitter [0, 0.5s]
The worker number is parsed from the CONSUMER_NAME env var (e.g., task_executor_abc123_5 → worker 5). Random jitter prevents thundering-herd scenarios where identically-delayed workers still collide. This was introduced alongside the Infinity pool-size reduction in PR #12006.
MCP Session Lifecycle#
MCPToolCallSession objects opened during agent canvas execution each spawn an event loop thread and hold an SSE/HTTP connection to an MCP server. Before PR #13295, these were never explicitly closed, accumulating indefinitely across canvas runs.
The fix adds Graph.close() to agent/canvas.py, which iterates all canvas components, identifies live MCPToolCallSession objects, and calls close_sync(timeout=3) on each (deduplication via seen = set()). This method is wired into canvas_service.py's streaming completion path inside a finally block , guaranteeing cleanup whether the canvas completes normally or raises an exception.
File Descriptor Leaks#
A systemic pattern of json.load(open(...)) — without closing the file — caused OSError: [Errno 24] Too many open files in long-running servers (PR #13997). The fix replaces bare open() calls with context managers in api/db/init_data.py and common/doc_store/infinity_conn_base.py.
Rule of thumb: always use with open(...) as f: for any file access in server code. The pattern json.load(open(...)) is invalid in long-running processes.
Key Source Files#
| File | Purpose |
|---|---|
common/http_client.py | Shared HTTP wrapper with retry/backoff |
rag/utils/opensearch_conn.py | OpenSearch singleton + pool config |
common/doc_store/infinity_conn_pool.py | Infinity connection pool (INFINITY_POOL_MAX_SIZE) |
rag/svr/task_executor.py | Worker startup staggering |
agent/canvas.py | Graph.close() for MCP session cleanup |
api/db/services/canvas_service.py | finally: canvas.close() in completion stream |
api/db/init_data.py | Corrected json.load file handle usage |