Skip to content

service-storage: StorageMetadataStore 把每一次引擎写入失败都静默吞掉并退回进程内存 —— sys_file 的「business truth」可能凭空消失且无一行日志 #5216

Description

@os-zhuang

#5202(把 sys_upload_session 加进 plugin-audit 的 SKIP_OBJECTS)里核对写入面时发现。不在 #5202 的 PR 里修(越界,一事一议;那单只动 packages/plugins/plugin-audit/src/)。

事实(对 origin/main @ b8503183c 核过)

packages/services/service-storage/src/metadata-store.ts 里,StorageMetadataStore全部 8 处引擎调用都套在同一个形状里 —— try { await this.engine.X(...) } catch { /* ignore */ },catch 块里没有任何 logger 调用,异常不外抛:

方法 调用 catch 注释
createFile() :83 engine.insert('sys_file', full) engine not available or schema not migrated — keep in-memory only
getFile() :94 engine.findOne('sys_file', …) fall through to memory
updateFile() :110 engine.update('sys_file', …)
deleteFile() :122 engine.delete('sys_file', …)
createSession() :146 engine.insert('sys_upload_session', full)
getSession() :157 engine.findOne('sys_upload_session', …)
updateSession() :178 engine.update('sys_upload_session', …)
deleteSession() :190 engine.delete('sys_upload_session', …)

每个写方法在调引擎之前都先写了进程内的 private readonly files = new Map(...) / sessions = new Map(...)(:68-69),读方法则「先引擎、失败或未命中就退回 Map」。类注释(:60-66)把这个 fallback 说成:

Backed by IDataEngine (objectql) when available — otherwise falls back to a process-local Map (suitable for tests and dev environments where the data engine isn't wired up).

问题:声明的意图与实际覆盖面不一致

注释和 catch 里的措辞说的是引擎不存在 / schema 未迁移这两种启动期状态。但 if (this.engine) 已经把「引擎不存在」挡在外面了,所以这些 catch 实际捕获的是引擎已接好之后的运行期失败 —— 约束冲突、连接抖动、RLS 拒绝、驱动错误。这类失败被:

  1. 不上报 —— 调用方拿到的是正常返回值(createFile 返回 full,updateSession 返回 merged),HTTP 层照样 200;
  2. 不记录 —— catch 块里没有 logger,this.engine 也没有被标记降级;
  3. 被内存 Map 掩盖 —— 紧随其后的 getFile() / getSession() 会从 Map 里读到那条「以为写成功了」的记录,所以同进程内自检也看不出异常。

后果按对象分两档:

为什么单独立单

我个人倾向 A:if (this.engine) 已经区分开了「没接引擎」与「接了引擎但写失败」,后者静默吞掉是把一个可诊断的错误变成一份丢失的业务真相,而 fallback 声称服务的对象(tests / dev)恰好是 this.enginenull 的那条分支 —— 也就是说这些 catch 对它们声称的用途并不必要。

未验证

我只做了静态阅读,没有构造一次真实的引擎写失败来观察端到端表现,也没有查是否有更上层的重试/补偿逻辑兜底。严重度请 PM 判。

Found-during: #5202 / PR #5215

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions