Skip to content

feat(file_storage): sync worker API, S3 multipart server puts, BIGINT sizes (#708) - #720

Merged
antosubash merged 4 commits into
mainfrom
feat/b33-file-storage-worker
Oct 30, 2026
Merged

antosubash merged 4 commits into
mainfrom
feat/b33-file-storage-worker

Conversation

@antosubash

Copy link
Copy Markdown
Owner

A public way for sync Celery workers to store and read files, and S3 server-side writes that work past 5 GiB.

Closes #708

What changed

  • Worker API in file_storage.contracts (sync, lazily imported, runs under the caller's tenant_context):
    • store_path(path_or_stream, *, filename=None, content_type=…, public=False) -> StoredFileOut goes through the normal upload path: size, MIME and quota checks, hashing, compensation. It runs in its own transaction and publishes FileUploaded after commit. An unscoped call fails before any IO.
    • open_local(file_id) is a context manager yielding a temp file in a private directory that is always cleaned up. The metadata transaction is closed before streaming.
    • presigned_url(file_id, ttl_seconds=…).
    • Reads reload by id under the tenant scope, so another tenant's id is "not found".
  • S3 put goes multipart above direct_upload_threshold_bytes, including unknown-length streams. Parts stay within S3's bounds, and any error, cancellation or failed completion aborts the upload without masking the original error.
    • A real file's size is now passed through, so parts grow to fit the 10,000-part limit. Without that, anything over about 156 GiB would fail.
    • Behaviour change: browser single-request uploads above the threshold use multipart too; the S3 credentials need s3:AbortMultipartUpload.
  • file_storage_stored_file.size_bytes is BIGINT (migration f5b1c3e8a207). On Postgres it was a 32-bit INTEGER, so any file of 2 GiB or more failed on insert, which would have made large server puts useless.
    • The migration also gives file_storage its own branch label, following CLAUDE.md's "label an unlabelled module on its next revision".
    • test_migration_graph, the migrations guide and CLAUDE.md's heads line are updated.
  • multipart_part_size_bytes is capped at 5 GiB (S3's per-part limit). A stored value above that is dropped with a warning instead of failing every multipart put.
  • A cancelled one-shot upload's metadata write now also deletes the uploaded object. Worker compensation treats a trashed row as committed.
  • Codex's first version added a server- key namespace that the reaper skipped. I removed it: the reaper only aborts untracked multipart uploads older than twice the session TTL (48 h), which no real put runs for, so server orphans are cleaned up like browser ones.

Verification

  • make lint ✓ · make test-js ✓ · make test-py ✓ (6641 passed, 41 skipped) · make migrations-roundtrip-pg ✓ (alembic check: no new operations)
  • New tests:
    • test_file_storage_worker.py: path and stream round trips, cross-tenant and missing ids, unscoped refusal, temp cleanup, remaining-size detection.
    • test_file_storage_worker_failures.py: quota, MIME, size, flush and commit failures, compensation.
    • test_file_storage_server_put.py: threshold boundaries, exact final part, unknown size, abort on stream, completion and part failures, part-count bound, part-size cap.
  • Code review: three passes; the last came back clean. Rejected findings are recorded with reasons.
  • Not tested against a live S3; the S3 client is faked at the wire level.

https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4

Cap multipart part size at S3's 5 GiB; trashed rows count as committed in
worker compensation; README matches the reaper.

Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
store_path declares a real file's size so S3 parts grow past the 10,000-part
limit; presigned TTL checked before the query; abort asserted on mismatch.

Claude-Session: https://claude.ai/code/session_01M9neheZZEe3sVpDi2S3zT4
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Deploying simple-module-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: 8bb8668
Status: ✅  Deploy successful!
Preview URL: https://7fdafa0e.simple-module-python.pages.dev
Branch Preview URL: https://feat-b33-file-storage-worker.simple-module-python.pages.dev

View logs

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review ✅ Completed 2026-10-30T23:23:02.533888Z 8bb8668 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@antosubash
antosubash merged commit a4d3c87 into main Oct 30, 2026
17 checks passed
@antosubash
antosubash deleted the feat/b33-file-storage-worker branch October 30, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

file_storage: public sync worker contract to store/open files (+ multipart for server-side puts > 5 GiB)

1 participant