-
Notifications
You must be signed in to change notification settings - Fork 92
feat(feeds): add per-channel timeout to FEED_REFRESH loop #892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -31,7 +31,9 @@ | |||||||||||||
| import java.util.*; | ||||||||||||||
| import java.util.concurrent.CompletableFuture; | ||||||||||||||
| import java.util.concurrent.ConcurrentLinkedQueue; | ||||||||||||||
| import java.util.concurrent.ExecutionException; | ||||||||||||||
| import java.util.concurrent.TimeUnit; | ||||||||||||||
| import java.util.concurrent.TimeoutException; | ||||||||||||||
| import java.util.regex.Pattern; | ||||||||||||||
| import java.util.stream.Collectors; | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -278,15 +280,27 @@ public void run() { | |||||||||||||
|
|
||||||||||||||
| for (String channelId : channelIds) { | ||||||||||||||
| try { | ||||||||||||||
| var info = ChannelInfo.getInfo("https://youtube.com/channel/" + channelId); | ||||||||||||||
| var tabInfo = ChannelHelpers.videosTabInfo(info); | ||||||||||||||
| if (tabInfo != null) | ||||||||||||||
| Multithreading.runAsync(() -> ChannelHelpers.federateChannelVideos(tabInfo)); | ||||||||||||||
| Multithreading.runAsync(() -> ChannelHelpers.federateChannelInfo(info)); | ||||||||||||||
| if (tabInfo != null) | ||||||||||||||
| ChannelHelpers.updateChannelVideos(info, tabInfo); | ||||||||||||||
| } catch (Exception e) { | ||||||||||||||
| ExceptionHandler.handle(e); | ||||||||||||||
| CompletableFuture.runAsync(() -> { | ||||||||||||||
| try { | ||||||||||||||
| var info = ChannelInfo.getInfo("https://youtube.com/channel/" + channelId); | ||||||||||||||
| var tabInfo = ChannelHelpers.videosTabInfo(info); | ||||||||||||||
| if (tabInfo != null) | ||||||||||||||
| Multithreading.runAsync(() -> ChannelHelpers.federateChannelVideos(tabInfo)); | ||||||||||||||
| Multithreading.runAsync(() -> ChannelHelpers.federateChannelInfo(info)); | ||||||||||||||
| if (tabInfo != null) | ||||||||||||||
| ChannelHelpers.updateChannelVideos(info, tabInfo); | ||||||||||||||
| } catch (Exception e) { | ||||||||||||||
| throw new RuntimeException(e); | ||||||||||||||
| } | ||||||||||||||
| }).get(60, TimeUnit.SECONDS); | ||||||||||||||
| } catch (TimeoutException e) { | ||||||||||||||
| System.out.println("FeedRefresh: timeout refreshing channel " + channelId + ", skipping"); | ||||||||||||||
| } catch (ExecutionException e) { | ||||||||||||||
| var cause = e.getCause(); | ||||||||||||||
| ExceptionHandler.handle(cause instanceof Exception ex ? ex : e); | ||||||||||||||
| } catch (InterruptedException e) { | ||||||||||||||
| Thread.currentThread().interrupt(); | ||||||||||||||
| break; | ||||||||||||||
|
Comment on lines
+301
to
+303
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Exit the refresh thread on interruption. Line 303 only breaks out of the Proposed fix } catch (InterruptedException e) {
Thread.currentThread().interrupt();
- break;
+ return;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| } | ||||||||||||||
| Thread.sleep(delay); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major
🧩 Analysis chain
🌐 Web query:
In Java 21, does CompletableFuture.get(long, TimeUnit) cancel or interrupt the asynchronous computation on TimeoutException, and does FutureTask.cancel(true) interrupt the runner?💡 Result:
In Java 21, the behavior is as follows: 1. CompletableFuture.get(long, TimeUnit) and TimeoutException: Calling CompletableFuture.get(long, TimeUnit) does not cancel or interrupt the underlying asynchronous computation when a TimeoutException is thrown [1][2]. The TimeoutException only indicates that the calling thread has stopped waiting for the result; the task continues to execute in the background [1][2]. To stop the computation, you must explicitly call cancel(true) on the CompletableFuture instance after catching the TimeoutException [1][2]. Alternatively, you can use the non-blocking orTimeout(long, TimeUnit) method to cause the CompletableFuture to complete exceptionally with a TimeoutException if it does not finish within the specified time [3][4]. 2. FutureTask.cancel(true) and the Runner: Yes, FutureTask.cancel(true) attempts to interrupt the thread currently executing the task [5][6]. If the task has already started, passing true to the cancel method signals that the thread running the task should be interrupted [5][6]. The implementation sets the task state to INTERRUPTING and calls thread.interrupt on the runner [7][8]. However, the success of this interruption depends on whether the task code is responsive to interrupts (e.g., it checks the interrupted status or calls blocking methods that throw InterruptedException) [1][2][9].
Citations:
🏁 Script executed:
Repository: TeamPiped/Piped-Backend
Length of output: 4114
🏁 Script executed:
Repository: TeamPiped/Piped-Backend
Length of output: 5590
Make the feed refresh job cancellable.
CompletableFuture.runAsync(...).get(60, TimeUnit.SECONDS)only times out the waiter; the refresh keeps running on the common pool and can still mutate channel state after being skipped. Use an interruptible task/thread and cancel it on timeout.InterruptedExceptionhandler only breaks the inner channel loop, so an interrupt does not stop the outer refresh loop.🤖 Prompt for AI Agents