Skip to content

fix(plugin-audit): 审计行写失败升为 error 并只报一次 (#5226 之二);#5226 主缺陷前提被证伪 - #5350

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5226-fresh-audit-table
Aug 5, 2026
Merged

fix(plugin-audit): 审计行写失败升为 error 并只报一次 (#5226 之二);#5226 主缺陷前提被证伪#5350
os-zhuang merged 1 commit into
mainfrom
claude/issue-5226-fresh-audit-table

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Refs #5226

ℹ️ PM 复核已把 Fixes 改为 Refs(2026-08-05)。 本 PR 只落地 #5226 的第二个缺陷(日志级别)。issue 陈述的主缺陷「表没建」已被本 PR 的 dev 证伪(表在 dev.telemetry.db 里,50 行),真实根因是跨数据源 ambient 事务泄漏,落点在 engine 车道,已另立单。#5226 不随本 PR 关闭。

前提证伪:表是建了的

issue 断言「表在这个全新数据库里根本没被创建」。在 origin/main(1792384)上跑真实复现,这一条不成立

pnpm dev:showcase -- --fresh -p 39726 --seed-admin --log-level debug

boot 日志里 provisioning 明确成功:

AuditPlugin: system tables provisioned — sys_audit_log→telemetry, sys_activity→telemetry,
                                         sys_comment→com.objectstack.driver.sql

直接查两个 SQLite 文件:

文件 是否有 sys_audit_log 行数
dev.db(主库)
dev.telemetry.db 50

sys_audit_loglifecycle.class: 'audit' 被 ADR-0057 §3.6 路由到专用 telemetry 数据源,os dev 默认以兄弟文件形式提供一个。表建好了,50 行审计也确实落盘了。#4887 早就记录过这个「在另一个 store 里」的误诊形状。

真实根因:ambient 事务跨数据源泄漏

同一次 boot 的完整计数(登录后做一次 PUT /api/v1/meta/api/issue5226probe):

  • sys_audit_log insert 尝试 52
  • 成功 50
  • 失败 2 次 —— 且这 2 次的堆栈全部带 knex 的 trxClient.query / execution/transaction.js

失败的两条正是 issue 贴出的那两条(sys_metadatasys_metadata_history)。机制:

  1. protocol.saveMetaItem主数据源上开启事务;
  2. 事务内写 sys_metadata 触发 afterInsert 钩子 → 审计写;
  3. getDriver('sys_audit_log') 正确解析到 telemetry 驱动;
  4. buildDriverOptions(packages/objectql/src/engine.ts:1609)把 txStore 里的 ambient 事务句柄无条件塞进 driver options —— 那个句柄属于主库连接;
  5. knex 的 .transacting(trx) 于是把语句发到主库执行,而主库没有这张表 → no such table: sys_audit_log

即 ADR-0067 D2「加入已开启的 ambient 事务」被应用时没有校验该事务是否属于同一个 driver

影响面比 issue 描述的宽

不限于元数据写。用 POST /api/v1/batch + transaction: true 写一条普通业务记录 showcase_category,同样丢审计行:

Audit write failed {"object":"showcase_category"}
Audit write failed {"object":"sys_metadata"}
Audit write failed {"object":"sys_metadata_history"}

结论:凡是在事务中执行的被审计写入,其合规审计行都会丢失 —— 只要该部署启用了 lifecycle 数据源分流(os dev 默认开;生产上设了 OS_TELEMETRY_DB 亦然)。这不是 plugin-audit 的问题,也不是「plugin 声明的对象没进 schema sync」:所有 plugin 声明对象都正常建表,sys_comment 因为没有 lifecycle class 留在主库、从不失败。受影响的是每一个 lifecycle class 为 audit/telemetry/event 的对象

本 PR 做了什么(只有第二个缺陷)

真实修复点在 packages/objectql/src/engine.ts 的事务管道 —— 属 engine 车道,在本单声明的文件面之外,且涉及一个 issue 未给出结论的架构取舍(跨数据源写入在外层事务回滚时应当:一起回滚?自动提交并接受孤儿审计行?还是直接拒绝?)。按借道声明与「不猜测公共契约」的规矩,此处停手并上报 needs_decision,不在本 PR 里动它。

本 PR 只落地 issue 的第二个论点 —— 日志级别,这一条完全在本单车道内且判据明确:

按 AGENTS.md「Degradation log levels」的那一个问题:降级之后系统从外面看是否仍然正常,而它声称已持久化的东西没落地?被审计的那次写入本身成功、数据在盘上、接口 200,只有记录「谁做的」的审计行没了,且无人重试 —— 是 durability/data-consistency 类,应为 error

验证

pnpm --filter @objectstack/plugin-audit test       → 7 files, 112 tests passed
pnpm --filter @objectstack/plugin-audit typecheck  → clean
node scripts/check-durability-degradation-log-level.mjs
  → 24 durability-critical catch seam(s), all loud or rethrowing

反向验证(方向为事前预测的 before-red / after-green):

  • reportAuditWriteFailure 改回 warn → 门禁按预期变红,精确点名 audit-writers.ts:729 guards persistAuditTrailRow()
  • 把 catch 体还原成改动前的那行 logger.warn('Audit write failed', …) → 新增 4 个用例中的 3 个变红(Tests 3 failed | 109 passed)。

诚实地说明第 4 个:never lets a logging failure break the audited write 改动前后都绿 —— 旧代码同样把日志调用包在 try 里。它固化的是一条本来就成立的性质,不是本次改动带来的新覆盖,保留是为了防止将来重构时丢掉。

遗留

主缺陷(跨数据源 ambient 事务)未修,#5226 需重新分诊并转派 engine 车道。


🤖 Generated with Claude Code

https://claude.ai/code/session_01FTszibd6C8sUCCZnM4VcrL


Generated by Claude Code


Generated by Claude Code

An audit-write failure is a durability / data-consistency degradation, not a
functional one: the audited write itself succeeds, its row is on disk and the
API returns 200, so nothing looks broken from the outside while the
`sys_audit_log` entry recording WHO did it never landed and nothing retries it.
AGENTS.md "Degradation log levels" puts that at `error`; it was at `warn`.

The error names both things such a line owes: the consequence (the compliance
trail is now incomplete, and the system will keep looking healthy) and the fix
(ADR-0057 lifecycle-class routing sends sys_audit_log to the `telemetry`
datasource when one is registered, so "no such table" here means the write ran
against a different datasource than the one holding the table; OS_TELEMETRY_DB=0
collapses the split).

Reported ONCE per process, not once per failed write — an audit write runs on
every mutation, and one error per write is what trained everyone to skim the
channel in #4420. Subsequent failures degrade to `debug`.

The write is extracted as a named `persistAuditTrailRow` callee and registered
in DURABILITY_CRITICAL_CALLEES so `pnpm check:durability-log-level` holds the
level; verified by reverting it to `warn` and watching the gate go red.

NOTE: this does NOT fix the missing-table symptom #5226 reports. That premise
was disproven on a real `dev --fresh` boot — the table IS created (in
dev.telemetry.db, 50 rows) — and the real defect is an ambient transaction
leaking across datasources in the engine. See the PR body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FTszibd6C8sUCCZnM4VcrL
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 12:19am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-audit.

4 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx (via @objectstack/plugin-audit)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/plugin-audit)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-audit)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-audit)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants