Skip to content

perf: cut startup job discovery from ~20h to ~2h on NFS - #11

Merged
yidakra merged 3 commits into
mainfrom
perf/fast-startup
Jul 15, 2026
Merged

perf: cut startup job discovery from ~20h to ~2h on NFS#11
yidakra merged 3 commits into
mainfrom
perf/fast-startup

Conversation

@yidakra

@yidakra yidakra commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

Every service (re)start did two sequential passes over all 143,384 video groups, one NFS round trip at a time — ~10 hours each (measured in the July 10/12 startup logs) before any GPU work began.

Changes

  • select_best_variant skips stats when the filename decides: it stat'ed every rendition file for a size tiebreak even when resolutions already ranked them; now it stats only genuine ties, making pass 1 metadata-free for typical _1080p…_180p groups.
  • Both startup passes run in a 64-thread pool (STARTUP_STAT_THREADS): stat/exists release the GIL and the work is pure NFS latency. 64 threads is deliberate — the NFS server also serves production playback.
  • New phase_needs() fuses needs_transcription + needs_translation into one stat per artifact (≤5 round trips per video instead of ~11). The separate functions remain for other call sites.

Test plan

  • uv run pytest — 102 passed; new exhaustive equivalence test pins phase_needs to the separate checks across every artifact presence/staleness combination
  • Benchmarked on the production archive over NFS: pass 1 ~3,200 groups/s (was ~1–4/s → ~45 s instead of ~10 h), pass 2 ~19–26 jobs/s (was ~4/s → ~1.5–2 h instead of ~10 h), measured while the old crawl was still competing for the same NFS server
  • Restart service on the merged code and confirm startup completes in ~2 h

Note: a stray architecture_diagram.png was accidentally committed and removed in the follow-up commit — squash-merge keeps it out of main entirely.

yidakra added 2 commits July 15, 2026 20:17
Startup did two sequential passes over 143k video groups, one NFS round
trip at a time (~10h each on this archive):

- select_best_variant stat'ed every rendition file even when the filename
  resolution already decided the winner; now stats only on ties, making
  the job-list build metadata-free for typical 5-rendition groups
- the job-list build and the two-phase filter now run their per-group
  checks in a 64-thread pool (STARTUP_STAT_THREADS) since stat/exists
  release the GIL and the work is pure NFS latency
- new phase_needs() fuses needs_transcription + needs_translation into
  one stat per artifact (5 round trips max instead of ~11 per video);
  equivalence with the separate checks is pinned by an exhaustive test
  over all artifact presence/staleness combinations

Measured on the production archive: pass 1 ~3200 groups/s (was ~1-4/s),
pass 2 ~19-26 jobs/s (was ~4/s).
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@yidakra, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d02d746d-b1cf-440a-9a95-c49683e9f3c1

📥 Commits

Reviewing files that changed from the base of the PR and between bed35ee and e0f2767.

📒 Files selected for processing (1)
  • src/python/tools/archive_transcriber.py
📝 Walkthrough

Walkthrough

Changes

Archive job evaluation

Layer / File(s) Summary
Concurrent startup discovery and variant selection
src/python/tools/archive_transcriber.py
Startup stat operations use a configurable thread count, per-group job checks run concurrently, and variant selection prioritizes resolution before file size.
Combined phase checks and parallel partitioning
src/python/tools/archive_transcriber.py, tests/test_archive_transcriber.py
phase_needs combines transcription and translation checks, run_two_phase evaluates jobs concurrently, and exhaustive tests compare combined results with the separate checks.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • yidakra/livevtt#1: Updates TTML-aware artifact handling in the same archive transcription job path.

Suggested reviewers: claude

Poem

I’m a rabbit with threads in my den,
Stat checks now race, then race again.
Resolutions hop to the front of the queue,
Phases sort what each job must do.
Fresh files gleam; stale ones go—
Faster burrows, steady flow!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main NFS startup performance improvement in the change set.
Description check ✅ Passed The description accurately matches the PR’s NFS startup optimization, threading, and phase-check changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@yidakra yidakra self-assigned this Jul 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/python/tools/archive_transcriber.py`:
- Around line 1009-1049: Update build_job and the should_skip interaction so
OSError from any per-group filesystem stat is contained to that group instead of
propagating through executor.map and aborting discovery. Catch the expected stat
failure around the should_skip call (or within should_skip if appropriate), log
the affected best_path, and return None so the existing discovery loop continues
processing remaining groups.
- Around line 2070-2092: Update the job-filtering block around check_job and
ThreadPoolExecutor to handle Ctrl+C without waiting for running phase_needs
calls or the executor context manager to drain. Replace the blocking
executor.map/context-manager pattern with the repository’s cancellation-friendly
pattern used by discover_video_jobs, while preserving job ordering and
transcription_jobs/translation_jobs population.
- Around line 1030-1049: The parallel job-building flow using ThreadPoolExecutor
and executor.map must remain interruptible during Ctrl+C. Replace the unbounded
map submission with bounded in-flight futures or explicitly cancel pending
futures on interruption, and ensure executor shutdown does not wait for queued
NFS stat/exists work before propagating the interrupt; preserve the existing job
collection and progress logging behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe84832a-6443-4b1a-b88e-64ea0cb5ddbb

📥 Commits

Reviewing files that changed from the base of the PR and between b88ed77 and bed35ee.

📒 Files selected for processing (2)
  • src/python/tools/archive_transcriber.py
  • tests/test_archive_transcriber.py

Comment thread src/python/tools/archive_transcriber.py Outdated
Comment thread src/python/tools/archive_transcriber.py Outdated
Comment thread src/python/tools/archive_transcriber.py Outdated
- build_job catches OSError so one transient NFS failure skips that group
  instead of aborting the whole discovery run
- both startup thread pools cancel their queued backlog on interrupt or
  error instead of draining all remaining groups before exiting
@yidakra
yidakra merged commit 3fb2d99 into main Jul 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant