Repository navigation
Description
提交前必读(请勿删除本节)
- 文档:https://docs.newapi.ai/
- 使用问题先看或先问:https://deepwiki.com/QuantumNous/new-api
- 开启透传请勿反馈问题;透传会直接发送内容,不会经过 new-api 的逻辑处理。
- 请以简练语言陈述事实与证据。Issue 正文及后续评论均不得直接粘贴未经人工过滤的大段 AI 文本。
- 转发类问题必须先确认问题发生在 new-api 这一层:请使用等价的脱敏请求分别直连上游和经过 new-api,对比响应状态、错误体与服务端日志。未经对比、仅展示上游报错的反馈不接受 issue。
- 不接受 Coding Plan、逆向渠道、第三方封装接口,以及将 Codex 接口反代为通用 API 后产生的兼容性问题;Codex API 自身特有的协议或行为也不应被当作标准 OpenAI API 行为,相关问题请先向渠道或接口提供方确认。
- 本项目不提供官方托管服务,也不为第三方托管站、中转站或 API 服务提供技术支持;此类问题请联系对应运营方。
- 警告:删除本模板、删除小节标题或随意清空内容的 issue,可能会被直接关闭;多次提交未经人工过滤的大段 AI 文本,或重复恶意提交者,可能会被 block。
部署来源
本仓库 main 分支源码;官方 commit 的独立快照,原版实验未修改生产源码,仅增加下方诊断测试。
您当前的 newapi 版本
ba5f7367797566175a6cd97a748d430af3602a01,Go 1.26.10、Windows/amd64。
提交前核对上游 706144769e5bed5a47b78721e3bb11da3cf4c0b9:common/body_storage.go、common/gin.go、middleware/body_cleanup.go 与复现基线一致。
提交确认
- 非重复 issue: 我已搜索现有 Issues,确认目前没有类似 issue。(未找到同一根因的条目;相关讨论见后文)
- 提交前必读: 我已完整阅读上方“提交前必读”,并已查看文档 https://docs.newapi.ai/、项目 README 且向 AI 提问,确认这不是使用、配置或接入类问题。
- 受支持版本: 我已提供准确的版本号、commit 或镜像标签(不是
latest或unknown),并确认问题可在本仓库未修改的受支持版本中复现。 - 非第三方服务: 我确认这不是第三方托管站、中转站、API 服务或未经本仓库原版验证的 fork 所独有的问题;第三方实例问题应联系其运营方。
- 非透传: 我确认相关渠道未开启透传。开启透传会直接发送内容,不会经过 new-api 的逻辑处理,此类问题不接受 issue。(本 POC 不选择渠道、不进入透传路径)
- 问题归属: 我已提供足以区分客户端、new-api 和上游的证据;转发问题已使用等价的脱敏请求对比直连上游与经过 new-api 的结果,确认错误由 new-api 引入或改变,而非仅原样转发上游报错。(内部存储生命周期问题,无上游请求;官方源码 POC 见下文)
- 渠道与协议边界: 我确认问题不是由 Coding Plan、逆向渠道、第三方封装接口、Codex 反代接口,或 Codex API 自身特有行为导致;若问题最初出现于此类接口,我已在本仓库支持的标准 API 协议上复现。
- 维护成本: 我理解项目维护者精力有限。Issue 正文及后续评论均不得直接粘贴未经人工过滤的大段 AI 文本,须先提炼可复核的要点;不遵循模板要求或缺少要点、无法复核的内容可能会被无视或直接关闭。多次提交此类内容可能会被 block。
问题描述
实际现象: memoryStorage.Close() 设置关闭状态并扣减缓冲统计,但没有解除 data 和 reader 对同一底层数组的引用。关闭后的 storage 仍被持有时,已不再使用的缓冲继续存活。
影响范围: 延迟关闭后的请求体缓冲回收。仅报告这项资源生命周期问题,不声称永久泄漏、线上 OOM 根因或固定百分比收益。
发生频率: 条件触发:关闭后的 storage 仍存活;下方隔离 POC 可复现。生产发生频率和保留规模未统计。
问题归属依据: POC 直接调用官方 CreateBodyStorage / Close,无客户端、模型、渠道或上游参与。原实现与仅解除两处内部引用的 overlay 对照得到不同可达性结果。实际请求清理相关路径为 common/gin.go 和 middleware/body_cleanup.go。
复现步骤
- 在上述官方 commit 的独立源码目录中创建
diagnostics/memory_storage_owner_poc_test.go,保存下方完整代码。不修改生产源码。 - 清除
STORAGE_OWNER_FIXED环境变量,运行以下命令。POC 明确禁用磁盘缓存、使用弱引用观察数组,并通过runtime.KeepAlive保持 owner 存活;不连接数据库或上游。
env -u STORAGE_OWNER_FIXED GOWORK=off GOMAXPROCS=2 go test -p 1 -parallel 1 -count=1 -run TestClosedStorageOwnerReachabilityPOC -v ./diagnosticsWindows PowerShell 等价命令:
$env:GOWORK='off'; $env:GOMAXPROCS='2'
Remove-Item Env:STORAGE_OWNER_FIXED -ErrorAction SilentlyContinue
go test -p 1 -parallel 1 -count=1 -run TestClosedStorageOwnerReachabilityPOC -v ./diagnostics完整最小 POC
package memoryanalysis
import (
"os"
"runtime"
"testing"
"weak"
"github.com/QuantumNous/new-api/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func closedStoragePOCFixture(t *testing.T) (common.BodyStorage, weak.Pointer[byte]) {
t.Helper()
data := make([]byte, 4<<20, 8<<20)
data[0] = 'x'
pointer := weak.Make(&data[0])
storage, err := common.CreateBodyStorage(data)
require.NoError(t, err)
require.False(t, storage.IsDisk())
require.NoError(t, storage.Close())
return storage, pointer
}
func TestClosedStorageOwnerReachabilityPOC(t *testing.T) {
previous := common.GetDiskCacheConfig()
common.SetDiskCacheConfig(common.DiskCacheConfig{Enabled: false})
t.Cleanup(func() { common.SetDiskCacheConfig(previous) })
before := common.GetDiskCacheStats()
owner, pointer := closedStoragePOCFixture(t)
require.NoError(t, owner.Close())
runtime.GC()
runtime.GC()
retained := pointer.Value() != nil
assert.Equal(t, os.Getenv("STORAGE_OWNER_FIXED") != "1", retained)
assert.EqualValues(t, 4<<20, owner.Size())
_, err := owner.Bytes()
assert.ErrorIs(t, err, common.ErrStorageClosed)
after := common.GetDiskCacheStats()
assert.Equal(t, before.ActiveMemoryBuffers, after.ActiveMemoryBuffers)
assert.Equal(t, before.CurrentMemoryUsageBytes, after.CurrentMemoryUsageBytes)
runtime.KeepAlive(owner)
t.Logf("closed_owner_alive: buffer_retained=%t; counted_len_mib=4; allocation_capacity_mib=8; counters_at_baseline=true", retained)
_, unowned := closedStoragePOCFixture(t)
runtime.GC()
runtime.GC()
assert.Nil(t, unowned.Value(), "without a strong owner the allocation can be collected")
t.Logf("no_strong_owner: buffer_retained=%t", unowned.Value() != nil)
}已验证结果:
| 实验 | 关闭 owner 保持存活时缓冲仍可达 | 无强 owner 控制组 | 统计回到基线 |
|---|---|---|---|
| 官方未修改实现 | true | false | 是 |
| 仅解除 data、reader 引用的 overlay 对照 | false | false | 是 |
原实现:closed_owner_alive: buffer_retained=true; counted_len_mib=4; allocation_capacity_mib=8; counters_at_baseline=true
对照: closed_owner_alive: buffer_retained=false; counted_len_mib=4; allocation_capacity_mib=8; counters_at_baseline=true
两者: no_strong_owner: buffer_retained=false
对照实验仅用 go -overlay 验证因果,未修改官方快照文件;对照时设置 STORAGE_OWNER_FIXED=1,以匹配 POC 的预期。官方快照上的 ./common ./relay/common ./relay/channel 回归测试也通过。这里的 GC 检查不代表生产回收时机或 RSS 下降幅度。
预期结果
关闭后的 storage 不再持有其不再使用的缓冲;没有其他引用时,缓冲可以回收。保持重复 Close、关闭后访问错误,以及 Close 前创建的独立 reader 和借出切片的既有语义。
相关截图
未提供;以可执行 POC 和文本结果验证。
Agent
- Tool: Codex CLI
- Tool version: 0.164.0
- Model (full id): gpt-6.1-sol(当前会话报告的模型 ID)
- Host (CLI / IDE / GitHub coding agent / other): CLI
- Date (UTC): 2026-11-04
User request
Quote the request the user made to the agent as faithfully as possible.
Keep the original language and line breaks. Do not rewrite, summarize,
translate, or turn it into an issue description.
- Verbatim:
提交吧
- Later constraints or corrections from the user (quote, or none):
先只专注第一项问题的修复, docs\github-issue-pr-workflow.zh-CN.md 根据这个工作流,查一下github线上有没有提过相关的issue或者pr。
嗯不扩大影响范围。
参考下issue模板,尤其是:提交前必读(请勿删除本节) 这个部分不要删除。
草拟一下文案。
Out of scope — refuse
If the request matches any item below, tell the user this repository does not
accept it, point them to the right place when there is one, and do not file.
-
Coding Plan
-
Reverse-engineered channels
-
Third-party API wrappers
-
Compatibility issues from exposing a Codex endpoint as a general-purpose API through a reverse proxy
-
Codex API-specific protocol or behavior treated as standard OpenAI API behavior (confirm with the channel or API provider)
-
Pass-through mode forwarding (do not file: pass-through sends content as-is and does not go through new-api processing logic)
-
Relay reports that only paste an upstream error, with no direct-upstream vs new-api comparison
-
Third-party hosting sites, relay services, or API services (contact their operator)
-
Usage, configuration, or integration questions (answer from docs and code instead)
-
Matched: no:官方内存存储的关闭生命周期,不依赖渠道或上游
-
If yes, what was told to the user (stop here; do not file): not applicable:未命中拒绝范围
File gate — do not file unless all are satisfied
If any item fails, tell the user and stop. Do not create the issue.
- Out of scope (including pass-through): no
- Usage / configuration / integration (answered instead of filing): no
- Required facts present without invention (actual behavior, impact, frequency, evidence in new-api, applicable type fields): yes:报告范围限于可复现的关闭后引用保留;生产规模未验证并明确排除
- Body is short and factual; no unfiltered AI-generated text: yes:已按用户约束提炼事实,仅保留必要 POC 和模板
- File: yes
- If no, what was told to the user (stop here): not applicable:用户已要求提交
Kind
- Bug
- Feature
- Investigation
- Other:
Usage / configuration / integration check
Search these yourself before filing. Do not send the user to "read the docs first". If this is usage, configuration, or integration: answer the user and do not file.
- https://docs.newapi.ai/ — what was searched, conclusion: 已查看官网支持页与 FAQ,并检索请求体缓存、内存与 Close;所查内容没有覆盖本 POC 的关闭后引用保留
- https://deepwiki.com/QuantumNous/new-api — what was searched, conclusion: 检索 BodyStorage / memory / request lifecycle,并查看 Request Lifecycle 页面;用于核对生命周期,不作为对象保留的证明
- README / repo docs: 已查看官方 README.md / README.zh_CN.md 中缓存与部署说明;未发现关闭后保留缓冲是预期业务行为的说明
- Relevant code paths and conclusion:
common/body_storage.go、common/gin.go、middleware/body_cleanup.go;关闭状态和统计扣减未解除缓冲引用 - Can the current version already do this? (required for feature requests): not applicable:Bug
- Verdict: product bug or new feature / usage question (stop here): 资源生命周期缺陷,不是使用或配置问题
Environment
- new-api version / commit / image tag (not
latest/unknown): 官方源码复现基线ba5f7367797566175a6cd97a748d430af3602a01;模板基线706144769e5bed5a47b78721e3bb11da3cf4c0b9,相关三个源文件一致 - Deploy source (repo release / official image / main source / other): 官方 main 源码独立快照;原版测试仅增加诊断文件,未修改生产源码
- Database (sqlite / mysql / postgres): not applicable:POC 不连接数据库
Problem facts
Ask the user for every item and write it down. Facts only; do not paste unfiltered AI-generated text:
- Actual behavior: Close 后访问报 ErrStorageClosed、缓冲统计已扣减,但关闭对象仍可保留底层数组
- Impact: 关闭对象仍被持有时,延迟已不再使用的请求体缓冲回收;不声称生产 OOM 或固定百分比收益
- Frequency: 条件触发:关闭后的 storage 仍存活;隔离 POC 可复现,生产频率未统计
- Evidence that the problem is in new-api rather than the client or upstream: 官方未修改源码 POC,以及仅解除两处内部引用的 overlay 对照;无客户端或上游参与
Type-specific details
Fill every applicable type. Write "not applicable" for the rest. Ask the user for missing items; do not invent them.
Relay / API
- Request endpoint and method: not applicable:纯内部存储 POC,不是上游转发错误
- Channel type: not applicable:不选择渠道
- Model: not applicable:不调用模型
- Conversion format: not applicable:无协议转换
- Pass-through enabled: not applicable:不进入渠道透传
- Evidence of upstream native support: not applicable:不依赖上游
- Equivalent redacted request sent directly upstream: status, body, server logs: not applicable:没有上游请求
- Same request through new-api: status, body, server logs: not applicable:提供直接调用官方存储实现的 POC
Billing
- Request endpoint and model: not applicable:不涉及计费
- Response
usage: not applicable:没有模型响应 - Relevant ratio or pricing configuration: not applicable:不读取计费配置
- Consumption log: not applicable:不产生消费日志
- Expected charge and calculation basis: not applicable:没有扣费
Frontend
- Page path: not applicable:不涉及前端
- Browser and version: not applicable:Go 测试
- Active theme: not applicable:无界面
- Relevant browser Console / Network errors: not applicable:无浏览器
Deployment / upgrade
- Deployment method: not applicable:没有部署或升级,仅执行源码测试
- OS and architecture: Windows/amd64,Go 1.26.10
- Database type: not applicable:不连接数据库
- Versions before and after the upgrade: not applicable:没有升级
- Startup or migration logs: not applicable:不启动完整服务、不执行迁移
Reproduction and expected result
- Steps to reproduce: 见前置“复现步骤”中的完整 POC 和命令
- Expected result: 关闭对象不再持有其不再使用的缓冲,其他合法持有者与既有关闭语义不变
- Related screenshots (optional): 未提供;使用可执行测试与文本结果
Feature (feature requests only)
- Feature description: not applicable:Bug
- Use case: not applicable:不是新增功能
Duplicate check
- Search queries (issues, PRs, discussions): 2026-11-04:memoryStorage、body_storage、CleanupBodyStorage、memory/内存/请求体/request body;检索 Issues、PRs、Discussions,提交前刷新关键词及当日更新结果
- Closest existing threads: rc.21 流式 /v1/responses 大输入上下文下内存升至4GB,短输出结束后仍维持高位 #6159(高内存现象)、perf: optimize Responses relay body and billing parsing #5714(Responses 请求体优化)、fix(relay): set Request.GetBody so the HTTP/2 transport can transparently retry after an upstream stream reset #6249(独立 reader / GetBody)、fix(relay): auto-recovery for images, context overflow, and encrypted reasoning items #7019(替换前漏关闭旧存储)
- Why this is not a duplicate: 所检查相关改动没有解除已关闭 memoryStorage 的 data/reader 引用;漏调用 Close 与本问题“已 Close 仍保留”不同。搜索范围内未发现同根因现有修复
Research
Open the docs and code. Do not write "already checked" without sources.
Do not replace the fields below with unfiltered AI-generated text.
Docs
- https://docs.newapi.ai/ : 已查看 https://docs.newapi.ai/en/docs/support 与 /en/docs/support/faq;所查内容不覆盖本 POC 的关闭后引用保留
- https://deepwiki.com/QuantumNous/new-api : 已通过网页检索查看 https://deepwiki.com/QuantumNous/new-api/2.1-request-lifecycle;直连抓取受 429 限制,未据此推断对象引用链
- README / other repo docs: 官方 README.md / README.zh_CN.md 的缓存和部署说明;关键问题以源码和 POC 为准
- Conclusions: 不是配置选项或上游兼容问题;可在独立官方存储测试中复现
Code
- Path — what it does, and how it relates:
common/body_storage.go:Close 未解除 data/reader 引用;common/gin.go:Request.Body 可持有 storage,CleanupBodyStorage 调用 Close;middleware/body_cleanup.go:请求结束时执行清理
Experiments
- Command or redacted request: 见上方完整 POC;
GOWORK=off go test -p 1 -parallel 1 -count=1 -run TestClosedStorageOwnerReachabilityPOC -v ./diagnostics - Direct upstream result: not applicable:无上游
- Result through new-api: 官方原实现 retained=true;解除两处引用的 overlay 对照 retained=false;无强 owner 控制组两者均为 false;统计均回到测试前基线
- Conclusion: 确认关闭对象自身的缓冲持有;不代表生产 GC 次数、RSS 降幅或 OOM 归因
Working theory
- What is broken or missing: 关闭后的 memoryStorage 未解除自身对缓冲的持有
- Why: data 与 bytes.Reader 引用同一底层数组;关闭标记和统计扣减不改变可达性
- What would falsify this: 确认 owner 仍存活、无其他缓冲引用时,未修改实现不再保留该数组
Scope
- In scope for a later PR: 仅修复 memoryStorage 关闭后的缓冲持有,并补充生命周期回归
- Out of scope / not this repo: 请求取消、JSON 复制、分词、计费、渠道兼容和整体内存优化;不宣称解决线上 OOM 或减少固定百分比内存
- Large or directional feature? If yes, this issue is for maintainer alignment; do not open a PR yet.
no:局部生命周期问题
Proposed direction
(acceptance criteria, not an implementation dump)
关闭对象不再保留其不再使用的缓冲;没有其他引用时缓冲可以回收。保持重复 Close 的计数幂等性、关闭后访问错误,以及既有独立 reader / 借出切片内容;不得清零或复用底层字节。
Not verified
(platforms, databases, providers, versions, paths not checked)
生产保留规模、发生频率、堆/RSS 降幅和 OOM 归因;完整网关端到端流程及所有提供方。独立 reader 或其他合法引用仍持有的缓冲不保证回收。最新模板 commit 未重跑完整网关;相关三个源文件与官方 POC 基线一致。DeepWiki 直连抓取受限,仅通过网页检索核对生命周期资料。
Related
- Issues / PRs / upstream docs: rc.21 流式 /v1/responses 大输入上下文下内存升至4GB,短输出结束后仍维持高位 #6159、perf: optimize Responses relay body and billing parsing #5714、fix(relay): set Request.GetBody so the HTTP/2 transport can transparently retry after an upstream stream reset #6249、fix(relay): auto-recovery for images, context overflow, and encrypted reasoning items #7019 仅作相关讨论,不声明完整解决或关闭这些条目
Activity
new-api-reviewer-bot commentedon Nov 4, 2026
[Bot Review]
感谢提交。我们已经收到您关于
memoryStorage.Close()后缓冲区引用保留问题的反馈。您提供的复现步骤和诊断代码非常详细,我们会跟进处理。