Repository navigation
perf(api): cache mixdown renders - #311
Merged
Merged
Conversation
Identical mixdown params re-ran the full ffmpeg graph on every request. On a shared server, repeat downloads of the same export (a common case) burned CPU for a pure function of the inputs. _stream_ffmpeg optionally tees yielded chunks to a per-request temp file as it streams; a clean finish atomically renames it into place as the cache entry and prunes the cache to a 20-file / 500 MB budget (oldest first). Any failure or client disconnect removes the temp file instead -- a render the client didn't get in full never becomes a cache hit for the next request. get_mixdown's cache key covers every render input (job_id, ext, stems, gains, region, and the live export sample rate setting), computed after the existing job/stem validation so a deleted or not-ready job still 404s the same way it always has instead of serving a stale entry. A hit returns a FileResponse with no ffmpeg invocation at all. Also: cache/ (CACHE_DIR's default under the repo root for source runs, same pattern as jobs/) wasn't gitignored -- added it alongside jobs/.
added 2 commits
July 17, 2026 14:37
- _prune_mixdown_cache: log a debug line instead of silently swallowing a failed unlink, so a stuck cache entry leaves a trace. - tests/test_stems_api.py: use "from app.api import stems as stems_mod" consistently instead of mixing it with "import app.api.stems as ...".
5 tasks done
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 #290, PR-3E (the stretch item) of Phase 3.
_stream_ffmpegoptionally tees yielded chunks to a per-request temp file while streaming; a clean finish (finished and returncode == 0) atomically renames it into place as the cache entry and prunes the cache to a 20-file / 500 MB budget (oldest evicted first). Any failure or client disconnect unlinks the temp file instead -- a render the client didn't get in full can never become a cache hit for the next request. The other 3_stream_ffmpegcall sites (stem region trim, stem-mp3, video mux) are untouched -- caching is opt-in via a newcache_pathparam, defaulting toNone.get_mixdown's cache key covers every render input: job_id, ext, stems, gains, region (start/end), and the liveexport_sample_ratesetting -- so a mixer change, a region trim, or a Settings change always misses cleanly. Computed after the existing job/stem validation, so a deleted or not-yet-done job still 404s exactly as before instead of serving a stale entry from a job that no longer exists.FileResponsedirectly -- no ffmpeg invocation, no subprocess spawn.cache/(whereCACHE_DIRdefaults to for source runs, same pattern asjobs/) wasn't gitignored. Added it -- caught this because an earlier local test run before I isolated the cache dir in tests wrote real cache files into the repo.Test plan
test_mixdown_cache_hit_skips_second_render-- two identical requests, second one doesn't invoke_stream_ffmpeg's ffmpeg subprocess at all, byte-identical response.test_mixdown_cache_key_varies_with_params-- different gains / region produce distinct cache entries, no collisions.test_mixdown_failed_render_leaves_no_cache_entry-- a corrupt input file (ffmpeg exits non-zero) leaves the cache dir empty, no stray temp file.test_prune_mixdown_cache_bounds_file_count/test_prune_mixdown_cache_bounds_total_size-- direct unit tests of the prune bound, oldest-first eviction.data/ffmpegon PATH), not just the CI-gated stub path -- 222 passed with ffmpeg available, 207 passed / 15 skipped without (matches the existing skip-gating convention for the other ffmpeg-happy-path tests in this file).ruff check/ruff format --checkclean