Let a caller take entries as they parse, instead of holding the catalogue - #8
Merged
Conversation
…ogue parseM3uStream never held the file, but it did hold every entry it kept, and on the list that prompted this -- 583MB, ~2.6 million entries -- that array alone is more heap than the container has. The byte ceiling the sites enforce upstream existed largely to keep this array survivable, so it was really a heap ceiling wearing a disguise. `onEntries` takes what each chunk produced and the parser forgets it. It is awaited rather than called per entry because `push` is synchronous and a consumer writing to a database has to be able to make the parse wait; that is what bounds an import to one batch of memory whatever the size of the list. `drain()` is the same thing on the line-at-a-time parser, and `kept` counts across drains so a drained parser does not forget it was full and start the ceiling again from zero. `max` of 0 or NaN now means unlimited rather than keep-nothing. An unset PLAYLIST_MAX_CHANNELS parses to 0, and reading that literally would turn a missing config line into an import that succeeds and stores no channels, which is the one failure mode nothing reports. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tn2NNUBNaAXJxZvq9bZRnH
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.
Published as
@profullstack/player@0.7.0. genrewatch and tipoffwatch both pin it.Why
parseM3uStreamnever held the file, but it did hold every entry it kept. On the list that prompted this — 583MB, roughly 2.6 million entries — that array alone is more heap than the container has. The byte ceiling the two sites enforce upstream was really guarding this array, so it was a heap ceiling wearing a disguise.What changed
onEntriesonparseM3uStream: takes what each chunk produced, then the parser forgets it. Awaited rather than called per entry, becausepushis synchronous and a consumer writing to a database has to be able to make the parse wait. That is what bounds an import to one batch of memory whatever the size of the list.drain()oncreateM3uParser: the same thing for the line-at-a-time form.keptcounts across drains, so a drained parser does not forget it was full and start the ceiling again from zero.maxof 0 or NaN now means unlimited, not keep-nothing. An unsetPLAYLIST_MAX_CHANNELSparses to 0, and reading that literally would turn a missing config line into an import that succeeds and stores no channels — the one failure mode nothing reports.MAX_CHANNELSstill defaults to 300,000 for callers that pass nomax; both sites now pass their own.Tests
125 pass,
tsc --noEmitclean, build clean. New coverage: entries handed over and forgotten; the peak held is a chunk rather than the list;onEntriesis not raced by the parse; a rejection from it aborts; the last entry (which lands on the flushed tail after the final chunk is drained) still arrives.🤖 Generated with Claude Code
https://claude.ai/code/session_01Tn2NNUBNaAXJxZvq9bZRnH