
claude-mem ChromaDB 子进程生命周期:健康检查、进程树清理与优雅降级设计【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem本文基于 claude-mem 仓库中的阶段规划文档 Phase-04-ChromaDB-Subprocess-Lifecycle.md,系统讲解该阶段如何修复ChromaMcpManager的发射后不管(fire-and-forget)子进程管理问题——进程泄漏、CPU 空转、僵尸累积与级联故障,并结合 src/services/sync/ChromaMcpManager.ts 等当前源码,验证这些规划在实现中的落地形态:连接退避、身份令牌验证的进程树回收、预热闸门与降级回退策略。一、阶段背景:为什么子进程生命周期是核心问题claude-mem 的语义(向量)搜索依赖一个外部 Python 子进程:由 Node worker 通过uvx启动chroma-mcp(ChromaDB 的 MCP server),两者经 MCP stdio 传输通信。阶段文档开宗明义地指出:This phase fixes the fire-and-forget subprocess management in the ChromaDB/MCP subsystem, which is the root cause of 8 issues: process leaks consuming CPU/memory, proxy failures, initialization races, and chroma-mcp zombie accumulation.即:子进程一旦卡死(CPU 自旋、死锁),父进程只能靠 transport close 事件感知死亡,而挂起状态下该事件根本不触发;加上没有资源限制和可靠清理,最终表现为 8 个以上 issue——内存泄漏、代理失败、初始化竞态、chroma-mcp僵尸进程堆积。从当前源码结构看,uvx - uv - python - chroma-mcp的四级进程链正是问题放大器:ChromaMcpManager.ts 第 190-196 行的注释明确记录了 #2313 的教训——MCP SDK 的transport.close()只向直接子进程(uvx)发信号,Linux 上孙辈进程(uv、python、chroma-mcp)会被重新挂到 init 名下存活,反复重连时一个会话可累积 20 个实例。二、Phase 04 的完整任务清单阶段文档定义了 5 项任务,覆盖监控—资源—清理—降级—验证全链路。以下完整继承原文档的任务规格,并在每节末尾给出当前仓库中的对应实现证据。2.1 任务一:健康监控与自动恢复原文档要求为ChromaMcpManager增加周期性健康检查,具体规格为:完整阅读src/services/sync/ChromaMcpManager.ts,理解全生命周期:ensureConnected()、callTool()、stop()以及 transport close 处理器(文档标注约 166-181 行);每 60 秒执行一次周期性健康检查,通过 MCP transport 发送一个轻量 no-op 调用(如chroma_list_collections),超时上限 10 秒;健康检查超时或连续失败 3 次时:以logger.error(CHROMA, Health check failed, restarting subprocess)记录失败;调用stop()杀死当前子进程;清空连接状态,使下一次callTool()触发ensureConnected()建立全新子进程;确保stop()中清理健康检查定时器,防止泄漏;复用现有RECONNECT_BACKOFF_MS(10 秒)避免健康检查触发的重启形成快速重启循环。当前实现对照:源码中RECONNECT_BACKOFF_MS 10_000确实存在(ChromaMcpManager.ts),且退避逻辑已实现——ensureConnected()在连接失败时记录lastConnectionFailureTimestamp,失败后 10 秒窗口内的新调用直接抛出ChromaUnavailableError提示剩余退避时间(第 150-185 行)。健康检查的轻量 no-op 调用思路也已被采纳:isHealthy()调用chroma_list_collections且limit: 1,成功返回 true,失败记录Health check failed并返回 false(第 872-882 行);更进一步,probeSemanticSearch()做深度探测:先chroma_list_collections统计集合数,再向cm__claude-mem集合发起一条query_texts: [ping]的查询,分connect / list / query / done四个阶段报告故障点并附带查询延迟(第 884-936 行);worker HTTP 层暴露健康端点:ChromaRoutes.ts 调用isHealthy()后返回healthy / unhealthy状态与细节说明,供 UI 与外部探针消费。从源码结构看,当前实现把健康检查做成了按需调用(HTTP 健康端点 深度探测),而原文档中60 秒周期 连续 3 次失败自动重启的定时器方案在ChromaMcpManager内未检索到对应定时器;可以推断周期性恢复职责由 supervisor/健康监控体系承担,连接层则以退避 传输错误重试实现自愈。2.2 任务二:初始化失败的 CPU 空转检测原文档指出:当uvx失败(找不到 Python、包下载错误、网络超时)时,子进程可能进入 busy-wait 循环、吃满 100% CPU。要求:阅读spawnChromaMcp()(文档标注约 188-234 行)理解启动方式;spawn 后 10 秒检查 CPU 使用率:Linux 读/proc/pid/stat,macOS 用ps -p pid -o %cpu;spawn 后 10 秒以上 CPU 超过 80% 即杀死子进程,并将 Chroma 标记为本 worker 会话内永久不可用,避免无限重启循环;增加maxConsecutiveFailures计数器(默认 3):连续 3 次连接失败后本会话禁用 Chroma,并记录日志:Chroma disabled after 3 failed connection attempts. Vector search unavailable — SQLite search still active.成功连接时重置该计数器。当前实现对照:CPU 空转的直接检测未在ChromaMcpManager中检索到对应实现,但失败即熔断的同类机制已用另一组手段落地:依赖健康记录:连接失败路径统一调用recordUvxVectorSearchUnavailable/recordChromaVectorSearchUnavailable(src/shared/dependency-health.ts),例如 uvx 探测失败时抛出ChromaUnavailableError并记录原因(第 213-217 行);成功连接后调用clearDependencyStatus(chroma)复位状态(第 296-297 行)——这与成功时重置计数器的意图一致;预热闸门防忙等:文档担心的初始化失败在实现中被前置为一轮带超时的uvx ... --help预热(prewarmChromaMcp,第 641-752 行)。预热命令由正式参数截断到chroma-mcp --help(第 583-589 行),超时取值CLAUDE_MEM_CHROMA_PREWARM_TIMEOUT_MS(默认 120_000ms,合法区间 1-600_000ms,非法值告警回退默认,第 26-28 行 与 第 591-624 行);预热失败/超时即用身份令牌校验后的killProcessTree杀掉整棵树并抛出ChromaUnavailableError,把坏环境挡在真正的 MCP 连接之前。2.3 任务三:进程树清理,消灭僵尸累积原文档指出:ProcessManager.ts的aggressiveStartupCleanup()在启动时立即杀chroma-mcp进程,但uvx派生的真正 Python 子进程可能随父进程死亡而存活。要求:阅读src/services/infrastructure/ProcessManager.ts中aggressiveStartupCleanup()(文档标注约 450-574 行)与cleanupOrphanedProcesses()(约 314-431 行);Unix:用kill(-pgid, SIGTERM)(进程组击杀)代替逐个 PID 击杀,确保uvx的全部子进程被终止;检查 spawn 时是否使用setsid(worker 用了,需验证 chroma-mcp);Windows:验证taskkill /T /F的/T标志能否穿透uvx/Python 子进程树;ChromaMcpManager.stop()中增加验证步骤:发送 SIGTERM/SIGKILL 后等待 2 秒再检查 PID 是否存活,存活则再次强杀;把 chroma-mcp 子进程 PID 以chromaPid字段写入 worker 的 PID 文件(~/.claude-mem/worker.pid),保证 worker 崩溃未清理时孤儿清扫也能找到它。当前实现对照:这是文档中落地最充分的一项。当前代码树击杀逻辑已集中到 src/shared/kill-process-tree.ts(提供killProcessTree、collectDescendantIdentities等原语),身份判定来自 src/supervisor/process-registry.ts 的captureProcessStartToken/isSameProcess/isPidAlive。核心实现在disposeCurrentSubprocess()(第 954-1053 行),其设计顺序值得细读:优雅优先,强杀兜底(#3540):先记录根进程 start token,再在 close 之前快照一次后代身份集合(snapshotDescendantIdentities),然后才调用transport.close()/client.close()。SDK 的 close 自带stdin EOF → 等 2 秒 → SIGTERM → 等 2 秒 → SIGKILL升级序列,正好对应文档要求的等 2 秒验证式宽限窗口;有界等待 exit 事件:waitForChildExit(child, CHROMA_EXIT_OBSERVE_TIMEOUT_MS)(1 秒)防止close()先于 Node 处理完exit事件返回,把已死进程误判为存活而过度升级、SIGKILL 掉正在构建中的uv(泄漏builds-v0/.tmp*临时目录);分平台升级策略:mustEscalate process.platform win32 || !exitedCleanly——Windows 上close()只对单个 PID 调 TerminateProcess,必须无条件走taskkill /T /F(即文档要求验证的树杀),POSIX 上仅在未干净退出时升级;双快照 身份校验回收:close 之后再做一次后代快照并与 close 前快照取并集回收(单个采样点都可能漏——close 前uv可能还没 fork,close 后孙辈已被 reparent);回收前用isSameProcess(pid, startToken)校验身份,令牌不匹配的 PID 会被跳过,避免误杀被操作系统复用的新进程;最后注销 supervisor 进程、释放 writer 锁、清空client/transport/connected状态——这正是任务一要求的清状态以便下次ensureConnected()重建。此外registerManagedProcess()(第 1489-1519 行)把子进程以pgid形式注册进 supervisor 的关停级联,POSIX 下用kill(-pgid, signal)一次系统调用拆除整条链,Windows 下回退taskkill /T——与文档中 Unix 进程组击杀的方案吻合。需要说明:文档引用的aggressiveStartupCleanup/cleanupOrphanedProcesses两个函数名在当前 ProcessManager.ts 中已检索不到,树杀职责已迁移到上述共享模块;worker.pid中的chromaPid字段也未在仓库中确认存在,不应作为既有能力引用。2.4 任务四:Chroma 不可用时的优雅降级原文档要求:阅读 src/services/worker/agents/ResponseProcessor.ts 中 fire-and-forget 式 Chroma 同步(文档标注约 195-218 行),以及 src/services/sync/ChromaSync.ts 的syncObservation()与queryDocuments();确认热路径(观察存储、搜索)上所有 Chroma 调用都有 try-catch 并回退 SQLite-only;增加chromaAvailable标志,禁用时整体跳过Chroma 调用而非每次尝试再捕获错误;可用 → 不可用切换时记一次日志:Chroma unavailable — falling back to SQLite-only search. Vector search disabled.;不可用 → 可用(重连成功)时记:Chroma reconnected — vector search restored.;搜索 API 响应包含chromaAvailable: boolean字段,让 UI 能指示当前搜索模式。当前实现对照:热路径的降级契约已在源码中得到印证。ResponseProcessor中对每条 observation 的 Chroma 同步是典型的不阻塞主流程写法——dbManager.getChromaSync()?.syncObservation(...).then(...).catch(...),catch 分支只记日志chroma sync failed, continuing without vector search,向量同步失败不影响 SQLite 存储与广播(第 619-654 行);syncSummary同构(第 720-742 行)。跳过而非试错则由前文提到的依赖健康体系承担:失败路径写入 unavailable 记录、成功路径clearDependencyStatus,上层据此短路 Chroma 操作。ChromaUnavailableError作为独立错误类型(src/services/worker/search/errors.ts)贯穿连接、预热、锁竞争等所有失败点,使调用方能区分Chroma 不可用与数据错误。至于chromaAvailable字段与切换日志的确切文案,当前仓库源码中未检索到逐字对应,属于规划规格而非已确认实现。2.5 任务五:测试与构建验证原文档列出的测试与验证要求:健康检查测试:mock 已连接 transport,模拟 3 次连续健康检查超时,验证触发子进程重启;CPU 空转检测测试:mock 高 CPU 子进程,验证超过阈值后被杀且 Chroma 被标记禁用;连续失败上限测试:模拟 3 次连接失败,验证本会话禁用 Chroma;优雅降级测试:mock Chroma 不可用,验证 observation 经 SQLite 存储成功、搜索返回 SQLite-only 结果;进程树清理测试:mock 带子进程的子进程,验证全部被杀死;构建验证:运行npm run build-and-sync、跑完整测试套件并修复失败、确认构建产物worker-service.cjs包含新的健康监控逻辑、在 Chroma 相关代码中搜索残留的 fire-and-forget 模式(.then().catch()且无错误状态管理)。当前仓库中的测试资产已覆盖其中相当一部分,可继续深入:tests/services/sync/chroma-mcp-manager-singleton.test.ts:围绕单例不变量(任何时刻至多一棵 chroma-mcp 进程树)、重连、uvx 缺失(uvx executable not found)等场景,大量用例经callTool(chroma_list_collections, ...)驱动连接-断开-重建循环;tests/integration/chroma-vector-sync.test.ts 与 tests/integration/chroma-windows-lifecycle.test.ts:向量同步与 Windows 平台生命周期;tests/shared/kill-process-tree-*.test.ts 系列(跨平台、进程身份、PID 复用等四个文件)直接为 2.3 节的令牌校验防误杀机制提供回归保障。三、关键常量与配置速查结合 ChromaMcpManager.ts 头部与配置读取逻辑,当前实现的默认值与可调项如下:常量 / 配置默认值说明RECONNECT_BACKOFF_MS10_000 ms连接失败后的重连退避窗口(即文档要求复用的退避常量)MCP_CONNECTION_TIMEOUT_MS30_000 msclient.connect(transport)的超时上限CLAUDE_MEM_CHROMA_PREWARM_TIMEOUT_MS120_000 msuvx 预热(--help)超时;合法范围 1-600_000,越界告警并回退默认CHROMA_MCP_PINNED_VERSION0.2.6锁定的 chroma-mcp 版本,经--from chroma-mcp0.2.6启动CHROMA_MCP_DEP_OVERRIDESonnxruntime1.20、protobuf7运行时依赖覆盖(#2371):前者保证能解析 all-MiniLM-L6-v2 的 pytorch-2.0 IR,后者避免 protobuf 7.x 拒绝 opentelemetry 的_pb2桩文件CLAUDE_MEM_PYTHON_VERSION3.13uvx 启动的 Python 版本前缀CLAUDE_MEM_CHROMA_MODElocallocal走--client-type persistent --data-dir chroma 目录并启用 writer 锁;remote走--client-type http,附带CLAUDE_MEM_CHROMA_HOST(默认 127.0.0.1)、_PORT(默认 8000)、_SSL、_TENANT、_DATABASE、_API_KEY参数(非默认值才追加)CLAUDE_MEM_CHROMA_MAX_PENDING_MUTATIONS5_000本地变更队列的在途上限;超出即拒绝并提示defer to a later backfillCLAUDE_MEM_CHROMA_UVX_PATH—直接指向 uvx 二进制的覆盖项;缺省时按已知 uv bin 目录解析(#2790:worker 继承的 PATH 可能不含 uv 目录,导致 uvx 25ms 内失败、语义搜索静默降级)其中buildCommandArgs()(第 385-427 行)完整演示了 local/remote 两套参数拼装;buildLauncherPrefix()生成--python ver --with onnxruntime1.20 --with protobuf7 --from chroma-mcp0.2.6 chroma-mcp前缀。另有两个容易忽视的细节:Windows 必须直接 spawn uvx.exe 而非经 cmd.exe 包装:依赖覆盖规格中的/会被 cmd.exe 解析为重定向,子进程约 10ms 内以 The directory name is invalid 死亡,语义搜索静默退化为关键词搜索(#2696)。resolveUvxCommand()(第 1348-1373 行)在非 Windows 返回裸uvx,在 Windows 解析uvx.exe绝对路径;spawn 环境卫生:getUvxPreflightEnv()经sanitizeEnv过滤环境变量、预置 uv bin 目录到 PATH、用stripForeignPythonEnv剥掉泄漏的 venv/conda 解释器(#3552 的 numpy ABI 冲突),并强制ANONYMIZED_TELEMETRYfalse关闭 Chroma 的匿名遥测;macOS 企业代理场景还会合成 certifi Zscaler 证书 bundle 注入SSL_CERT_FILE等变量。四、并发与多实例防护:writer 锁与变更队列阶段文档聚焦生命周期,但实现中还有两块生命周期相关的防护,值得补充:Chroma writer 锁(acquireChromaWriterLock,第 429-502 行):local 模式下在数据目录以wx独占标志创建.claude-mem-chroma-writer.lock,内容含 pid、ownerId、acquiredAt 与 startToken。锁已存在时:本进程旧锁则复用;持有者已死(PID 不存活或 startToken 不匹配)则判定为陈旧锁删除重取;持有者仍活则抛ChromaUnavailableError拒绝启动第二个写者——从机制上保证每个数据目录同时只有一棵 chroma-mcp 进程树。变更串行化队列:callTool()对匹配chroma_(add|create|delete|modify|update|upsert)_的写类工具(第 40 行)走enqueueMutation()串行队列(local 模式下),在途数量受maxPendingMutationCalls限制,超限抛ChromaUnavailableError让调用方延迟到后续 backfill;队列条目还携带connectionGeneration,关停期间排队的变更会被显式取消而非误发给旧连接。读类调用则不走队列,直接并发。五、要点回顾单例不变量是设计主线:任何要放弃当前 transport 的路径(重连、传输错误、连接超时、onclose、stop())都必须经过disposeCurrentSubprocess(),先树杀、再清状态,保证每个 worker 至多一棵 chroma-mcp 进程树;身份令牌是安全护栏:startToken 在进程存活时采集并与 PID 成对保存(见TrackedChild注释,第 79-102 行),所有延迟清理都携带它,防止 PID 复用导致误杀无关进程树;优雅优先、强杀兜底:宽限窗口交给 SDK 的 close 升级序列,POSIX 仅在未干净退出时升级,Windows 无条件树杀——分平台差异被显式建模;降级契约:热路径上的 Chroma 调用一律可失败、可跳过,SQLite 关键词搜索始终是兜底;依赖健康记录 ChromaUnavailableError让上层能区分不可用与数据错误;阶段文档中的aggressiveStartupCleanup/cleanupOrphanedProcesses(ProcessManager)、60 秒周期健康定时器、chromaPid落盘 PID 文件等条目,在 2026-09 时点的仓库中未检索到逐字对应实现,属于规划规格或已被现有机制替代的方案,引用时注意以当前源码为准。【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考