Skip to content

fix(rest): make async import-job cancellation actually stop the worker (#2824)#3069

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-2824-verification-7f1524
Jul 16, 2026
Merged

fix(rest): make async import-job cancellation actually stop the worker (#2824)#3069
os-zhuang merged 1 commit into
mainfrom
claude/issue-2824-verification-7f1524

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Summary

Fixes #2824 — cancelling a running async CSV import had no effect: the job kept writing all rows and finished succeeded.

Root cause: with a synchronous storage driver (better-sqlite3 / wasm fallback), every await in the import row loop resolves as a microtask, so a 50k-row import monopolizes the Node event loop for its whole duration. The cancel route's HTTP handler (and every progress poll) never gets to run, so the in-memory flag that shouldCancel polls is never set.

Three-part fix:

  • runImport yields one macrotask (setImmediate) at every progress boundary (every progressEvery rows), so pending I/O — the cancel request, progress polls — gets serviced during a large import. Root-cause fix; also unblocks live progress for the wizard.
  • The worker's shouldCancel falls back to reading the durable job row: a cancel accepted by another process (or after a restart dropped the in-memory flag) still stops the worker.
  • A late cancel wins the terminal state: the final patch never overwrites the cancel route's durable cancelled with succeeded, and a job cancelled while still pending doesn't start at all.

Testing

  • 3 new unit tests (import-runner-cancel.test.ts): mid-loop cancel stops at the next checkpoint with truthful counts; no-cancel path unaffected; yield happens even without shouldCancel.
  • 3 new integration tests (import-job-integration.test.ts): mid-flight cancel via the route; out-of-band durable cancel (no in-memory flag); late-cancel terminal-state guard.
  • Negative verification: 4 of the 5 new tests fail on pre-fix code.
  • Full @objectstack/rest suite green (288 tests), build + DTS clean.
  • End-to-end showcase UI run on the rebuilt dist: live progress renders during a 50k import (dead at 0% before), clicking 取消导入 stops the worker mid-run (job cancelled at 17600/50000, row count stable afterwards), and an uncancelled control import still succeeds 50000/50000.

Known remaining (out of scope): the wizard shows 「已导入 0 条」 after cancel instead of the actual partial count — objectui frontend issue (objectui#2393).

Co-Authored-By: Claude noreply@anthropic.com

@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 16, 2026 2:44pm

Request Review

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/error-catalog.mdx (via @objectstack/rest)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest)
  • content/docs/api/index.mdx (via @objectstack/rest)
  • content/docs/plugins/index.mdx (via @objectstack/rest)
  • content/docs/plugins/packages.mdx (via @objectstack/rest)
  • content/docs/releases/implementation-status.mdx (via @objectstack/rest)
  • content/docs/releases/v12.mdx (via @objectstack/rest)

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.

@os-zhuang
os-zhuang merged commit 7bc9e79 into main Jul 16, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-2824-verification-7f1524 branch July 16, 2026 14:59
#2824)

On a synchronous storage driver (better-sqlite3 / wasm fallback) every await
in the import row loop resolves as a microtask, so a 50k-row import starved
the Node event loop for minutes: the cancel route's HTTP handler (and every
progress poll) never ran, the in-memory flag `shouldCancel` polls was never
set, and the job finished `succeeded` with all rows written despite the
user's cancel.

- runImport now yields one macrotask at every progress boundary so pending
  I/O (cancel requests, progress polls) is serviced mid-import.
- The worker's shouldCancel also reads the durable job row as a fallback,
  covering cancels accepted by another process or after a restart.
- A late cancel wins the terminal state: the worker's final patch no longer
  overwrites the durable 'cancelled' with 'succeeded', and a job cancelled
  while still pending doesn't start at all.

Co-Authored-By: Claude <noreply@anthropic.com>
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