perf(video): buffer upload metadata in a bounded bytearray, not a list[bytes] - #9583
Open
lstein wants to merge 1 commit into
Open
perf(video): buffer upload metadata in a bounded bytearray, not a list[bytes]#9583lstein wants to merge 1 commit into
lstein wants to merge 1 commit into
Conversation
…t[bytes] The multipart parser hands the `metadata` field over in whatever pieces the client sent it, and `_VideoUploadStreamParser` kept one `bytes` object per piece. Per-object overhead then dominated: a client that dribbled the field in 2-byte chunks retained ~22x the payload, so the 1 MiB `MAX_UPLOAD_METADATA_SIZE` cap bounded payload but not memory (~22 MiB per upload, x2 concurrent slots). Accumulate into a `bytearray` instead, the way the header buffers already do. The buffer's own length is now the size the cap checks, so the separate `_metadata_size` counter goes away, and `_on_part_end` decodes the buffer directly. Tests: feed a 64 KiB field 1, 2 and 3 bytes at a time and assert retained allocation stays under 2x the payload (the old code retained 8.6x / 21.7x / 15.0x); and check the cap still counts the whole field across chunks. Closes invoke-ai#9563 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5Y1XMvgKRCr2pu6wm8rs1
lstein
requested review from
JPPhoto,
Pfannkuchensack,
blessedcoolant and
dunkeroni
as code owners
September 11, 2026 01:55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #9563 (follow-up to the non-blocking note in JPPhoto's review of #9396).
_VideoUploadStreamParserbuffered themetadatapart aslist[bytes], one object peron_part_datacallback. The multipart parser hands the field over in whatever pieces the client sent it, so a client that dribbles the field in tiny chunks makes CPython's per-object overhead dominate:MAX_UPLOAD_METADATA_SIZEbounded the payload but not the retained memory.This accumulates into a
bytearrayinstead, the way_header_field/_header_valuein the same class already do. The buffer's own length is now what the cap checks, so the separate_metadata_sizecounter is gone, and_on_part_enddecodes the buffer directly instead ofb"".join(...).Measured
Retained allocation (tracemalloc) for a 64 KiB metadata field, as a multiple of the payload:
One-byte chunks are not the worst case, contrary to the issue's estimate: CPython interns single-byte
bytesobjects, so only the list slot is retained. Two-byte chunks are the worst case, at ~22x. With the 1 MiB cap and two concurrent upload slots, that is ~44 MiB of avoidable retention per pair of hostile uploads; after this change it is ~2.2 MiB.Accept/reject behaviour of the cap is unchanged:
len(buffer) + len(chunk) > MAXis the same predicate as the old running counter.Tests
test_upload_metadata_buffer_is_bounded_by_its_size_not_its_chunk_count[1|2|3]: feeds a 64 KiB field 1, 2 and 3 bytes perparser.write()and asserts retained allocation stays under 2x the payload. Verified load-bearing: against the previous implementation these fail with 8.6x / 21.7x / 15.0x. 64 KiB rather than the full 1 MiB because per-bytewrite()calls are ~11 s at 1 MiB, and the amplification ratio is size-independent.test_upload_metadata_cap_counts_the_whole_field_across_chunks: a field exactly at the cap split unevenly across chunks is accepted and reassembled intact; one byte over, where no single chunk is near the cap, is still rejected with 413.Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_01L5Y1XMvgKRCr2pu6wm8rs1