Skip to content

fix(objectql): Archiver 的批循环尊重 #4747 的 teardown abort 位 - #5956

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-5755-archive-abort-check
Aug 6, 2026
Merged

fix(objectql): Archiver 的批循环尊重 #4747 的 teardown abort 位#5956
baozhoutao merged 2 commits into
mainfrom
claude/issue-5755-archive-abort-check

Conversation

@baozhoutao

@baozhoutao baozhoutao commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #5755

前提复核(先于实现)

按 issue 的 lead 逐条对 origin/main(a6b3ee7a1)实读,前提成立(行号已漂,按内容定位):

检查点 分诊记录(f205c32) 本次实读(a6b3ee7a1) 结论
sweep() 对象之间 :571 / :598 :571 if (this.stopped) return report;:598 同款
batchedReap 每页之间 :1202(PR #5753) :1202 if (this.abort.aborted) break;
archiveObject 批之间 :1047–1056 确无 :1047for 批循环(上界 ARCHIVE_MAX_BATCHES_PER_SWEEP)循环体直接以 await hot.find(...) 开头 确无

一轮 archive 的量级也与正文一致:20 批 × 500 行,每批 = 1 次 hot.find + 每行 1 次 cold.upsert + 1 次 hot.bulkDelete,即最多约一万次跨两个 datasource 的读写。stop() 落在其中任何一点,循环都会把剩余批次跑完 —— 这正是 #4747 停掉的形状,reap 那侧已由 #5753 补上,archive 这侧没有。

改动

archiveObject() 批循环体开头一行,与 :1202 同款:

if (this.abort.aborted) break;

外加一段注释,写清两件事:为什么 sweep() 的对象间检查不够(一轮 archive 是 20 页跨两个 datasource 的读写),以及为什么按批 break 不破坏 archive 的安全规则「归档成功才热删」—— 在飞的那一批会把 upsertbulkDelete 这一对做完,未开始的批次原样留在热库,交给下一轮 sweep 重新读。没有重构循环本身。

⛔ 同文件的 #5756(reap 每轮预算按「(对象, where 作用域)」计)是语义取舍、分诊持有中,本 PR 未触碰

测试(packages/objectql/src/lifecycle/lifecycle-service.test.ts)

驱动真 LifecycleService + 计数 stub 冷热双 driver(每一条腿都记账:页读、逐行拷贝、热删),加在 teardown (#4747) describe 里,与 stop() mid-reap 那条并列:

  1. 对照 —— an archive nobody calls off runs its whole 20-batch budget:不抬 abort 位时页读 20 次、拷贝 10 000 行、热删 20 批,余下 500 行留给下一轮。
  2. 主用例 —— stop() mid-archive ends the batch loop instead of running out the page budget:在第 2 批的第一行 cold.upsert 处抬起 abort 位,断言
    • 页读只有 2 次(第 3–20 批一次 hot.find 都没发);
    • 拷贝恰好 1000 行,热删 [500, 500];
    • 成对不变量:hotDeleted.flat() 逐项等于 copied —— 冷库收下的每个 id 都被热删,且没有任何未被冷库收下的 id 被热删,两个方向都没有半批;
    • 余下 9500 行仍在热库,等下一轮。

archive.keep 有意未在该 fixture 上声明:冷侧 prune 是循环之后的单次调用,本 PR 不改那条腿,fixture 也就不去断言它。

反向验证(方向先预测,后运行)

预测:标准红 —— 去掉新检查后主用例转红,对照用例保持绿(它从不抬 abort 位,本就无法区分两个版本,这正是它作为对照而非第二个 pin 的原因)。

实跑与预测一致:

✓ ... > an archive nobody calls off runs its whole 20-batch budget 22ms
× ... > stop() mid-archive ends the batch loop instead of running out the page budget 29ms
  → expected [ 500, 500, 500, 500, 500, 500, …(14) ] to have a length of 2 but got 20
 Test Files  1 failed (1)

恢复该行后 72/72 全绿。

验证

pnpm --filter @objectstack/objectql typecheck   → tsc --noEmit,无输出(通过)
pnpm --filter @objectstack/objectql test        → Test Files 128 passed | Tests 2118 passed
node scripts/check-nul-bytes.mjs                → OK(5723 个文件,无裸控制字节)
node scripts/check-engine-double-contract.mjs   → OK — 70 pinned, 133 DEBT, 2 exempt
eslint(两个改动文件)                            → 无输出

上述数字取自 git merge origin/main(⛔ 未 rebase)之后的重跑 —— 合入的 metadata-protocol/protocol.tsobjectql/protocol-meta.test.ts 与本改动包重叠,故按 AGENTS §9/§10 重建依赖后整包重跑。

新测试未引入新的 fake engine:复用文件内既有的 captureEngine;新增的冷热 stub 是 driver 替身(find/upsert/bulkDelete/deleteMany),不含引擎 delete/update 派发面,check:engine-double-contract 复跑仍为 OK。

影响面

今天不可达:archive 策略要求已配置 cold datasource,仓内无平台对象声明 archive,未配置时直接 skipped: 'archive-pending'。这是为首个声明 archive 的部署做的预防,changeset 按 @objectstack/objectql patch 记。


🤖 Generated with Claude Code

https://claude.ai/code/session_019Q7oc7ASjh8yxyS3Yz78We

claude added 2 commits August 6, 2026 12:47
`LifecycleService.stop()` raises an abort bit checked at every leg boundary --
between objects in `sweep()`, and between reap pages since #5753. The
Archiver's own batch loop had no such check, so teardown landing mid-archive
ran the remaining batches out: up to 20 x 500 rows of `hot.find` + per-row
`cold.upsert` + `hot.bulkDelete`, i.e. ~10k operations issued across two
datasources the host is already closing. That is the #4747 defect itself, on
the one loop #5753 did not reach.

Same one-line check as `batchedReap`, at the top of the batch body. Breaking
BETWEEN batches preserves the Archiver's safety rule ("hot-delete only what the
cold store took"): the batch in flight completes its upsert -> bulkDelete pair,
and batches not yet begun are left for the next sweep to re-read.

Not reachable today (no platform object declares `archive`), so this is
prevention for the first deployment that does.

Fixes #5755

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

vercel Bot commented Aug 6, 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 6, 2026 12:55pm

Request Review

@github-actions github-actions Bot added the size/m label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql.

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

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/objectql)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • content/docs/plugins/index.mdx (via @objectstack/objectql)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql)
  • content/docs/protocol/objectql/query-syntax.mdx (via packages/objectql)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/objectql)

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.

finding(objectql): Archiver 的 20 批循环不看 #4747 的 abort 位 —— teardown 期间仍会继续读写正在关闭的 datasource

2 participants