Skip to content

fix(storage): abort backend multipart when reaping abandoned sys_upload_session (#2970)#2999

Merged
os-zhuang merged 1 commit into
mainfrom
claude/attachments-v1-followups-8jhvin
Jul 16, 2026
Merged

fix(storage): abort backend multipart when reaping abandoned sys_upload_session (#2970)#2999
os-zhuang merged 1 commit into
mainfrom
claude/attachments-v1-followups-8jhvin

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes the last sub-follow-up on #2970 (follow-up to merged #2984).

The leak

The sys_upload_session lifecycle (added in #2984) reaps abandoned/terminal chunked-upload session rows, but not the underlying backend multipart upload. On S3 an initiated-but-not-completed multipart keeps its already-uploaded parts billable and invisible to normal listing until an explicit AbortMultipartUpload — so reaping only the row stranded them, with backend_upload_id (the sole pointer) gone.

The fix

createUploadSessionReapGuard registers a LifecycleReapGuard on sys_upload_session (same seam as the sys_file guard) that aborts the backend multipart before the row is deleted:

  • completed sessions → skip abort (their multipart already became a real object; an abort would NoSuchUpload-error and wedge the reap) — just reap the row.
  • no backend_upload_id / adapter can't abort → nothing to abort, reap the row.
  • in_progress / failed / expired with a backend upload → re-seed the S3 adapter's uploadId → key map from the row (a cold sweep lacks the live in-process map that setUploadKey populates during upload; reached via getInner() since it's S3-specific and not forwarded by the swappable proxy), then abortChunkedUpload. Confirm on success; veto (keep the row for retry) on failure so the pointer survives. The local adapter's parts dir is removed the same way.

The abort API already existed (IStorageService.abortChunkedUpload, S3 AbortMultipartUploadCommand, local parts-dir rm); this wires it into the sweep.

Verification

  • Unit (@objectstack/service-storage 99 ✓): 7 new guard tests — S3 key re-seed + abort + reap, completed-skip, no-backend_upload_id, veto-on-abort-failure, local adapter (no getInner/setUploadKey), no-abort adapter; plugin-wiring test asserts both sys_file and sys_upload_session guards register.
  • Dogfood: the attachments matrix drives a real chunked upload (initiate → upload one chunk → part on disk under <rootDir>/.parts/<uploadId>/) → backdate expires_atlifecycle.sweep() → asserts the session row is gone and the parts dir was aborted (removed). Matrix 15 ✓; the full dogfood suite runs as this PR's Dogfood Regression Gate.
  • Gates: check:api-surface ✓ (internal export only).

ℹ️ As before, the red Validate Dependencies check is the repo-wide retired-npm pnpm audit endpoint — unrelated to this diff.

With this, all of #2970 — the six items plus this sub-follow-up — is complete.

🤖 Generated with Claude Code


Generated by Claude Code

…ad_session (#2970)

The sys_upload_session lifecycle (#2984) reaps abandoned/terminal
chunked-upload session rows but not the underlying backend multipart —
on S3 an initiated-but-not-completed multipart keeps its parts billable
and invisible until AbortMultipartUpload, so reaping only the row
stranded them (backend_upload_id, the sole pointer, gone).

createUploadSessionReapGuard registers a LifecycleReapGuard on
sys_upload_session that aborts the backend multipart before the row is
deleted: skips 'completed' sessions (multipart already an object — an
abort would NoSuchUpload-error), re-seeds the S3 uploadId->key map from
the row (cold sweeps lack the live in-process map), and vetoes (keeps
the row for retry) on abort failure so the pointer survives. Local
adapter parts dir removed the same way.

Verified: 7 new unit tests (S3 key re-seed, completed-skip, no-backend,
veto-on-failure, local adapter, no-abort adapter); service-storage 99;
dogfood matrix drives a real chunked upload → part on disk → sweep →
row gone AND parts dir aborted. Closes the last #2970 sub-follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187NT3Qer9oep5dCRb9b8Lt
@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 3:19am

Request Review

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/dogfood, packages/services.

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

  • content/docs/automation/webhooks.mdx (via packages/services)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/permissions/authorization.mdx (via packages/dogfood)
  • content/docs/permissions/delegated-administration.mdx (via packages/dogfood)
  • content/docs/plugins/packages.mdx (via packages/services)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services)

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 marked this pull request as ready for review July 16, 2026 03:22
@os-zhuang os-zhuang merged commit 3a10503 into main Jul 16, 2026
17 checks passed
@os-zhuang os-zhuang deleted the claude/attachments-v1-followups-8jhvin branch July 16, 2026 03:22
os-zhuang added a commit that referenced this pull request Jul 16, 2026
…ion guard note (#3009)

Documents the attachment access model landed across #2984/#2999/#2538
(#2970): parent-derived read/create/delete, inherited list visibility,
authenticated parent-scoped downloads, the enable.files opt-in gate, the
member_default no-delete-baseline caveat, the storage-byte lifecycle
(sys_file + sys_upload_session reap guards), and an enforcement-summary
+ error-code table. Registered in permissions/meta.json.

Also names sys_upload_session as the second reap-guard consumer in the
ADR-0057 amendment.


Claude-Session: https://claude.ai/code/session_0187NT3Qer9oep5dCRb9b8Lt

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