fix(memory): clear bodyString after req.write() to prevent closure capture

Additional memory optimizations:
- Set bodyString = null after req.write() in both stream and non-stream requests
- Use let instead of const for bodyString to allow nullifying
- Store non-stream originalBodyString in bodyStore to avoid closure capture
- Clean up bodyStore in finally block for non-stream requests

This prevents V8 closures (res.on handlers) from retaining large request
body strings until stream completion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
root
2026-01-12 08:57:54 +00:00
parent f535b35a1c
commit b26027731e

View File

@@ -382,6 +382,7 @@ class ClaudeRelayService {
let queueLockAcquired = false let queueLockAcquired = false
let queueRequestId = null let queueRequestId = null
let selectedAccountId = null let selectedAccountId = null
let bodyStoreIdNonStream = null // 🧹 在 try 块外声明,以便 finally 清理
try { try {
// 调试日志查看API Key数据 // 调试日志查看API Key数据
@@ -542,8 +543,10 @@ class ClaudeRelayService {
const isRealClaudeCodeRequest = this._isActualClaudeCodeRequest(requestBody, clientHeaders) const isRealClaudeCodeRequest = this._isActualClaudeCodeRequest(requestBody, clientHeaders)
const processedBody = this._processRequestBody(requestBody, account) const processedBody = this._processRequestBody(requestBody, account)
// 🧹 内存优化:存储序列化字符串用于重试,避免重复转换工具名 // 🧹 内存优化:存储到 bodyStore避免闭包捕获
const originalBodyString = JSON.stringify(processedBody) const originalBodyString = JSON.stringify(processedBody)
bodyStoreIdNonStream = ++this._bodyStoreIdCounter
this.bodyStore.set(bodyStoreIdNonStream, originalBodyString)
// 获取代理配置 // 获取代理配置
const proxyAgent = await this._getProxyAgent(accountId) const proxyAgent = await this._getProxyAgent(accountId)
@@ -571,12 +574,12 @@ class ClaudeRelayService {
let shouldRetry = false let shouldRetry = false
do { do {
// 🧹 每次重试从字符串解析新对象,避免使用被修改的 body // 🧹 每次重试从 bodyStore 解析新对象,避免闭包捕获
let retryRequestBody let retryRequestBody
try { try {
retryRequestBody = JSON.parse(originalBodyString) retryRequestBody = JSON.parse(this.bodyStore.get(bodyStoreIdNonStream))
} catch (parseError) { } catch (parseError) {
logger.error(`❌ Failed to parse originalBodyString for retry: ${parseError.message}`) logger.error(`❌ Failed to parse body for retry: ${parseError.message}`)
throw new Error(`Request body parse failed: ${parseError.message}`) throw new Error(`Request body parse failed: ${parseError.message}`)
} }
response = await this._makeClaudeRequest( response = await this._makeClaudeRequest(
@@ -916,6 +919,10 @@ class ClaudeRelayService {
) )
throw error throw error
} finally { } finally {
// 🧹 清理 bodyStore
if (bodyStoreIdNonStream !== null) {
this.bodyStore.delete(bodyStoreIdNonStream)
}
// 📬 释放用户消息队列锁(兜底,正常情况下已在请求发送后提前释放) // 📬 释放用户消息队列锁(兜底,正常情况下已在请求发送后提前释放)
if (queueLockAcquired && queueRequestId && selectedAccountId) { if (queueLockAcquired && queueRequestId && selectedAccountId) {
try { try {
@@ -1431,7 +1438,7 @@ class ClaudeRelayService {
return prepared.abortResponse return prepared.abortResponse
} }
const { bodyString, headers, isRealClaudeCode, toolNameMap } = prepared let { bodyString, headers, isRealClaudeCode, toolNameMap } = prepared
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 支持自定义路径(如 count_tokens // 支持自定义路径(如 count_tokens
@@ -1545,6 +1552,8 @@ class ClaudeRelayService {
// 写入请求体 // 写入请求体
req.write(bodyString) req.write(bodyString)
// 🧹 内存优化:立即清空 bodyString 引用,避免闭包捕获
bodyString = null
req.end() req.end()
}) })
} }
@@ -1846,7 +1855,7 @@ class ClaudeRelayService {
return prepared.abortResponse return prepared.abortResponse
} }
const { bodyString, headers, toolNameMap } = prepared let { bodyString, headers, toolNameMap } = prepared
const toolNameStreamTransformer = this._createToolNameStripperStreamTransformer( const toolNameStreamTransformer = this._createToolNameStripperStreamTransformer(
streamTransformer, streamTransformer,
toolNameMap toolNameMap
@@ -2627,6 +2636,8 @@ class ClaudeRelayService {
// 写入请求体 // 写入请求体
req.write(bodyString) req.write(bodyString)
// 🧹 内存优化:立即清空 bodyString 引用,避免闭包捕获
bodyString = null
req.end() req.end()
}) })
} }