fix(io): latch the live join shape on rejection instead of assuming it (#331 follow-up) - #336
Merged
Merged
Conversation
#331 follow-up) A live reconnect asked `bytes=<frontier>-` and starved against panels that answer 416 to any offset they cannot satisfy. #332 made every live request `bytes=0-`, which fixes those panels but is wrong for the other live shape: a source that IS a growing file (a Jellyfin live stream file, a misdeclared VOD) answers the frontier correctly and resumes there, and asking it for byte zero re-delivers its whole buffer on top of the window. The rejection is the signal. Keep the frontier until an origin answers 416 to a nonzero live offset, latch the join shape from that point, and name the latch in the log so a field capture separates "this origin has no byte addresses" from "the reconnect is broken". A ring-buffer panel pays exactly one rejected request per reader; a byte-addressable live source pays nothing and keeps resuming. Both directions witnessed by disabling the guard: never latching starves after one 4 MB burst (13 rejected requests at the same offset, the field trace), latching unconditionally restarts a range-honouring live origin at byte zero. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HX5zV3Fcf7Nzq4DYGdXvQE
Merged
4 tasks
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
Follow-up to #332 (merged). The live request shape is now decided by the origin's answer instead of by the
isLiveflag.#332 made every live persistent request
bytes=0-. That is right for a ring-buffer IPTV panel, which has no byte addresses and rejects a frontier with 416. It is wrong for the other live shape: a source that IS a growing file (a Jellyfin live stream file, a misdeclared VOD) answers the frontier correctly and resumes exactly where delivery stopped, and asking that one for byte zero re-delivers its whole buffer on top of the window. Both shapes reachAVIOReaderthroughLoadOptions.isLive, which is a host declaration, so the flag cannot tell them apart.What changed
bytes=0-) for the rest of the reader's life. A ring-buffer panel pays exactly one rejected request per reader; a byte-addressable live source pays nothing.Test plan
swift test: 1623 tests green.LiveWindowBackpressureTestscovers both origin shapes. Witness-checked in both directions by disabling the guard: never latching starves after one 4 MB burst (13 rejected requests at the same offset, reproducing the field trace); latching unconditionally makes the range-honouring origin's refill restart at byte zero.Reported in #331 (kept open until the reporter retests on device)
🤖 Generated with Claude Code
https://claude.ai/code/session_01HX5zV3Fcf7Nzq4DYGdXvQE