Make streaming multipart decode O(n) - #128
Conversation
iter_series stream=True rescans the whole buffer on every chunk when a series is one large part. This test requires streaming to stay within a small factor of buffered decode. Co-authored-by: Cursor <cursoragent@cursor.com>
Resume find() across chunks instead of rescanning and copying the whole buffer on every iter_content chunk. Refs ImagingDataCommons#127 Co-authored-by: Cursor <cursoragent@cursor.com>
pieper
left a comment
There was a problem hiding this comment.
Generally this looks good to go - thanks for the fix!
Co-pilot made a few suggestions. I won't paste them in here but you could ask it yourself and see if you agree. Mostly style and a few edge performance issues.
|
@pieper sure! Do you mind if I ask where those suggestions are? |
|
Here you go. Like I said, I don't think it's too critical but probably worth having a look. Copilot Chat Conversation Export: Reviewing PR for red flags andUser: @pieper @pieperAny red flags in this PR? CopilotSummary — quick answer
What I did
PR summary
Core change (what and why)
Relevant snippets (context from the PR) delimiter = b''.join((b'\r\n', marker))
data = bytearray()
j = 0
+ search_from = 0
with response:
logger.debug('decode message content')
if stream:
@@
- prev_part_index = 0
- while True:
- delimiter_index = data.find(delimiter, prev_part_index)
+ prev_part_index = 0
+ while True:
+ delimiter_index = data.find(delimiter, search_from)
if delimiter_index < 0:
break
@@
prev_part_index = delimiter_index + len(delimiter)
+ search_from = prev_part_index
j += 1
@@
- data = data[prev_part_index:]
+ if prev_part_index:
+ data = data[prev_part_index:]
+ search_from = max(0, len(data) - len(delimiter) + 1)Other change: new test file # tests/test_streaming.py added: a parametrized test that builds a single large multipart body,
# then compares time for buffered (stream=False) vs streamed (stream=True) retrieval,
# asserting streamed is < 2x slower than buffered up to 40MB payloads.Merge readiness and risk assessment
Actionable review comments
Possible improvements (suggestions tied to code/tests)
Notes on edge cases and potential risks
Small stylistic nit
Want me to…
If you want, I can implement suggestion 2 (draft the extra tests) and/or suggestion 3 and push a small follow-up patch here in the PR. Which would you like me to do next? |
Keep only 20/40MB timing cases; smaller sizes are timer noise. Trim the buffer in place and assert a split delimiter still decodes. Co-authored-by: Cursor <cursoragent@cursor.com>
|
|
@pieper applied most of the changes. The timing thing I don't think it applies now, as the tests are rather quick (actually streaming is now quicker than buffering by 2x 😛), and as the test compares the time one takes against the other, even on high loads they'll tend to be the same. I removed the obvious cases (payloads <= 15MB) and only left the hefty ones. Thanks for the review 🙏 |
|
Hi @pieper kind reminder about the status of this PR's code review 🙏 |
|
github had some issues but it seems to be back now. @CPBridge could you take a quick look at this? |
|
@pieper if you are onboard we can merge and put out a patch release |
|
Thanks @CPBridge 👍 |



Fixes #127
Streaming
_decode_multipart_messagenow resumesfind()across chunks instead of rescanning and copying the whole buffer on every chunk.running with
uv run pytest tests/test_streaming.py -s -vyou can see the comparison over buffered vs streamingbefore the change