fix(dvr): bound the timeshift window so one request can't OOM the host#16
Merged
Conversation
Reader.Query resolved the timeshift window end to +inf whenever `dur` was absent (the common case) or non-positive: the hour filter then admitted every hour and the loop built a FragmentRef per fragment for the WHOLE archive — and ServeMPD does this for EVERY profile. A single unauthenticated request like GET /<code>/index.mpd?from=0 (or ?ago=<huge>) therefore allocated and read hundreds of MB on a multi-day, multi-profile archive, and a few concurrent requests OOM-kill the shared process, taking down every stream on the host. The 120 s request timeout couldn't help — Query held no context. Resolve the window through resolveWindowBounds: clamp the depth to MaxTimeshiftWindow (24h default, a package var so ops can tune and tests can shrink it) when dur is absent/non-positive/oversized, and pull the start UP to the archive's earliest hour so from=0 / ago=<huge> can't anchor at the epoch and admit every hour. The open-ended 1<<62 sentinel is gone — the end bound is always finite. Query now takes a context.Context and checks ctx.Err() at the top of the hour loop, so the server timeout and client disconnects abort a long scan; both handler call sites pass r.Context(). Tests: TestResolveWindowBounds (clamp table — open-ended/oversized dur capped, from-below-earliest pulled up) and TestBlobReader_QueryClampsAndCancels (from=0 still returns the archive via the earliest-hour clamp; a cancelled context aborts the scan). Also marks B-3 fixed-by-A-3 in the audit: the codec-aware AV-path keyframe gate + hevc/hevc_cuvid decoder rebuild already make HEVC AV transcode work, so the "silent zero output" path is resolved (doc-only update here).
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
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.
Problem (audit A-2, HIGH)
Reader.Queryresolved the timeshift window end to+inf(1<<62) wheneverdurwas absent (the common case) or non-positive. The hour filter then admitted every hour and the loop built aFragmentRefper fragment for the whole archive — andServeMPDrepeats this for every profile. So a single unauthenticatedGET /<code>/index.mpd?from=0(or?ago=<huge>) allocated + read hundreds of MB on a multi-day, multi-profile archive; a few concurrent requests OOM-kill the shared process and take down every stream on the host. The 120 s request timeout couldn't abort it —Queryheld no context.Fix
resolveWindowBounds(pure, table-tested): clamp depth toMaxTimeshiftWindow(24h default; a package var so ops can tune / tests can shrink) whenduris absent / ≤0 / oversized, and pullfromup to the archive's earliest hour (earliestHourMs) sofrom=0/ago=<huge>can't anchor at the epoch and admit every hour.1<<62sentinel is gone —endMs/endTicksare always finite (pairAudio/snapVideounchanged, just always take the bounded path).Querynow takescontext.Contextand checksctx.Err()at the top of the hour loop, so the server timeout + client disconnect abort a long scan. Both handler call sites (ServeTimeshift,ServeMPD's per-profile loop) passr.Context().Tests
TestResolveWindowBounds(clamp table: open-ended/negative/oversized dur → capped to max; from-below-earliest → pulled up; in-range dur preserved) andTestBlobReader_QueryClampsAndCancels(afrom=0query still returns the archive via the earliest-hour clamp; a cancelled context aborts the scan withcontext.Canceled).go test -race ./internal/dvr/blob/green,golangci-lint0 issues, fullgo build ./...green.Note
Also marks B-3 ✅ fixed-by-A-3 in the audit (doc-only): the codec-aware AV-path keyframe gate +
hevc/hevc_cuviddecoder rebuild fromfix/decoder-codec-awarealready make HEVC AV transcode decode instead of silently dropping every packet.