Skip to content

fix: stream large unsigned PUTs once (deferred SigV4)#116

Merged
ServerSideHannes merged 7 commits into
mainfrom
fix/large-put-stream-once-in-flight-cap
Jul 8, 2026
Merged

fix: stream large unsigned PUTs once (deferred SigV4)#116
ServerSideHannes merged 7 commits into
mainfrom
fix/large-put-stream-once-in-flight-cap

Conversation

@ServerSideHannes

@ServerSideHannes ServerSideHannes commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix Scylla SST OOM: large PUTs without x-amz-content-sha256 no longer call request.body() before streaming — credentials are checked up front, body is hashed once during encrypt/upload, SigV4 verified before complete.
  • Single-pass _iter_request_body avoids double-buffer when a small body was preloaded for signature (request.body() + request.stream()).
  • Dockerfile uses python -m s3proxy.main so TLS settings from Settings apply in prod.

Not included

  • No S3PROXY_MAX_IN_FLIGHT — memory governor (S3PROXY_MEMORY_LIMIT_MB) already handles backpressure.
  • No polling is_disconnected() during streaming (Starlette can drop body bytes and corrupt SHA256).

Test plan

  • uv run pytest tests/unit/ -q — 534 passed
  • CI: ruff, unit, helm-lint, all integration shards (core, multipart, memory_usage, memory_copy, memory_leak, copy_range, misc)
  • test_scylla_deferred_put_memory.py — 179MB deferred PUT peaks ~33MB, not ~358MB double-buffer
  • Post-deploy: large Scylla PUTs log signature_deferred (not body_loaded ~179MB); OOM rate drops on server pods

ServerSideHannes and others added 6 commits July 8, 2026 11:33
Defer SigV4 verification for large bodies missing x-amz-content-sha256 so
Scylla SST uploads hash while streaming instead of double-buffering in RAM.
Add uvicorn limit_concurrency (S3PROXY_MAX_IN_FLIGHT) to bound httptools
socket buffers outside the memory governor, abort multipart on client
disconnect, and route the Docker entrypoint through main.py so limits apply.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use `is True` / isinstance(bytes) for deferred-sig and preloaded-body
so MagicMock request.state does not trigger deferred verify or buffer
errors in integration tests. Rename ClientDisconnectError for N818;
treat only explicit True from is_disconnected() as a disconnect.

Co-authored-by: Cursor <cursoragent@cursor.com>
Auto-derived limit_concurrency (memory_limit_mb // 8) rejected connections
before the memory governor could queue them, breaking backpressure integration
tests and causing 503/partial-upload SHA256 mismatches. Default 0 is now
unlimited; set S3PROXY_MAX_IN_FLIGHT explicitly in prod when needed.

Co-authored-by: Cursor <cursoragent@cursor.com>
track_chunk() called request.is_disconnected() every 8MB while reading
the upload body. Starlette can dequeue http.request messages during that
check and drop body bytes, corrupting SHA256 on large signed PUTs.

Remove the polling path; rely on ClientDisconnect from request.stream()
and abort multipart on disconnect. S3PROXY_MAX_IN_FLIGHT stays opt-in
only (0 = unlimited, unchanged from main).

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Not required for the OOM fix; memory governor already handles
backpressure. Drops config, chart wiring, tests, and uvicorn
limit_concurrency plumbing.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ServerSideHannes ServerSideHannes changed the title fix: stream large unsigned PUTs once and cap in-flight connections fix: stream large unsigned PUTs once (deferred SigV4) Jul 8, 2026
Makes _parse_header_auth() return a named type instead of an opaque
8-element tuple, improving readability at all three call sites.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ServerSideHannes ServerSideHannes merged commit 3d3248f into main Jul 8, 2026
10 checks passed
@ServerSideHannes ServerSideHannes deleted the fix/large-put-stream-once-in-flight-cap branch July 8, 2026 12:09
ServerSideHannes added a commit that referenced this pull request Jul 8, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant