feat(feeds): add consecutive-failure backoff to FEED_REFRESH loop#893
feat(feeds): add consecutive-failure backoff to FEED_REFRESH loop#893jollySleeper wants to merge 1 commit into
Conversation
When YouTube rate-limits an instance (429, CAPTCHA, or bot-detection), the current FEED_REFRESH loop catches the error and immediately moves to the next channel. This means it can fire off dozens of failed requests in quick succession against a service that is actively rejecting it. Add a simple consecutive-failure counter: after 5 failures in a row (strongly suggesting rate-limiting rather than individual channel issues), pause for 5 minutes before resuming. Any successful refresh resets the counter. This is deliberately not exponential backoff — the loop already has built-in per-channel pacing, so a single fixed pause is sufficient to let rate-limit windows expire without adding complexity. Note: Piped's DownloaderImpl does not currently throw ReCaptchaException on 429 responses (the handler is commented out), so rate-limiting manifests as ParsingException or IOException when the extractor fails to parse YouTube's error page. A type-agnostic consecutive-failure counter handles this correctly regardless of the specific exception type.
📝 WalkthroughWalkthroughFeed-refresh processing now counts consecutive channel-refresh failures, resets the counter on success, and pauses for five minutes after five back-to-back exceptions before resuming. ChangesFeed-refresh failure backoff
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/main/java/me/kavin/piped/Main.java`:
- Around line 279-280: The failure streak in the refresh loop is being reset
each time the outer while pass restarts, so move consecutiveFailures to a scope
that survives across full refresh passes in Main’s refresh logic. Update the
handling around the loop that processes subscribed channels so the same counter
is reused across iterations, and keep the backoff reset only when a successful
refresh occurs; this ensures the streak can cross pass boundaries and trigger
the intended delay.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8cb7b320-3dbc-4916-b84b-736b2b1b4ea7
📒 Files selected for processing (1)
src/main/java/me/kavin/piped/Main.java
| int consecutiveFailures = 0; | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep the failure streak across full refresh passes.
consecutiveFailures is recreated on each outer while iteration, so a streak that spans the end of one pass and the start of the next is lost. That means instances with fewer than 5 subscribed channels can fail forever without ever hitting the new backoff.
Proposed fix
if (Constants.FEED_REFRESH)
Thread.ofVirtual().name("feed-refresh").start(() -> {
+ int consecutiveFailures = 0;
while (true) {
try {
List<String> channelIds;
@@
- int consecutiveFailures = 0;
-
for (String channelId : channelIds) {
try {
var info = ChannelInfo.getInfo("https://youtube.com/channel/" + channelId);Also applies to: 290-299
🤖 Prompt for 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.
In `@src/main/java/me/kavin/piped/Main.java` around lines 279 - 280, The failure
streak in the refresh loop is being reset each time the outer while pass
restarts, so move consecutiveFailures to a scope that survives across full
refresh passes in Main’s refresh logic. Update the handling around the loop that
processes subscribed channels so the same counter is reused across iterations,
and keep the backoff reset only when a successful refresh occurs; this ensures
the streak can cross pass boundaries and trigger the intended delay.
Summary
FEED_REFRESHloop — after 5 failures in a row, pauses for 5 minutes before resumingProblem
When YouTube rate-limits an instance (HTTP 429, CAPTCHA pages, or bot-detection), the current loop catches the error and immediately moves to the next channel with only the normal per-channel delay. With hundreds of subscribed channels, this means dozens of failed requests in quick succession against a service that is actively rejecting the instance.
Why not exponential backoff?
The loop already has built-in per-channel pacing (
window / channel_count), so a single fixed 5-minute pause is sufficient to let rate-limit windows expire. Exponential backoff adds state management complexity for marginal benefit in this architecture.Why not catch
ReCaptchaExceptionspecifically?The current
DownloaderImpldoes not throwReCaptchaExceptionon 429 responses — the CAPTCHA handling code is commented out. Rate-limiting manifests asParsingExceptionorIOExceptionwhen the extractor fails to parse YouTube's error/CAPTCHA page. A type-agnostic consecutive-failure counter handles this correctly regardless of exception type.Changes
Main.java: AddconsecutiveFailurescounter scoped to each refresh cycleTest plan
./gradlew build)FEED_REFRESH=trueand verify normal refresh cycle works (counter stays at 0)Summary by CodeRabbit