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-zhuangos-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.db50

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

vercelBot commented Aug 5, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 5, 2026 12:19am

Request Review

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

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude