Compare commits

...

3 Commits

Author SHA1 Message Date
spinline
f3121898e2 fix: wake up background polling loop immediately when a client connects
All checks were successful
Build MIPS Binary / build (push) Successful in 4m22s
2026-02-09 00:22:28 +03:00
spinline
e1370db6ce ui: rename Down Speed to DL Speed in torrent table
All checks were successful
Build MIPS Binary / build (push) Successful in 4m26s
2026-02-09 00:07:07 +03:00
spinline
1432dec828 perf: implement dynamic polling interval based on active clients
All checks were successful
Build MIPS Binary / build (push) Successful in 4m35s
2026-02-08 23:57:32 +03:00
3 changed files with 30 additions and 11 deletions

View File

@@ -45,6 +45,7 @@ pub struct AppState {
pub db: db::Db, pub db: db::Db,
#[cfg(feature = "push-notifications")] #[cfg(feature = "push-notifications")]
pub push_store: push::PushSubscriptionStore, pub push_store: push::PushSubscriptionStore,
pub notify_poll: Arc<tokio::sync::Notify>,
} }
async fn auth_middleware( async fn auth_middleware(
@@ -336,6 +337,8 @@ async fn main() {
#[cfg(not(feature = "push-notifications"))] #[cfg(not(feature = "push-notifications"))]
let push_store = (); let push_store = ();
let notify_poll = Arc::new(tokio::sync::Notify::new());
let app_state = AppState { let app_state = AppState {
tx: tx.clone(), tx: tx.clone(),
event_bus: event_bus.clone(), event_bus: event_bus.clone(),
@@ -343,6 +346,7 @@ async fn main() {
db: db.clone(), db: db.clone(),
#[cfg(feature = "push-notifications")] #[cfg(feature = "push-notifications")]
push_store, push_store,
notify_poll: notify_poll.clone(),
}; };
// Spawn background task to poll rTorrent // Spawn background task to poll rTorrent
@@ -351,6 +355,7 @@ async fn main() {
let socket_path = args.socket.clone(); // Clone for background task let socket_path = args.socket.clone(); // Clone for background task
#[cfg(feature = "push-notifications")] #[cfg(feature = "push-notifications")]
let push_store_clone = app_state.push_store.clone(); let push_store_clone = app_state.push_store.clone();
let notify_poll_clone = notify_poll.clone();
tokio::spawn(async move { tokio::spawn(async move {
let client = xmlrpc::RtorrentClient::new(&socket_path); let client = xmlrpc::RtorrentClient::new(&socket_path);
@@ -359,6 +364,14 @@ async fn main() {
let mut backoff_duration = Duration::from_secs(1); let mut backoff_duration = Duration::from_secs(1);
loop { loop {
// Determine polling interval based on active clients
let active_clients = event_bus_tx.receiver_count();
let loop_interval = if active_clients > 0 {
Duration::from_secs(1)
} else {
Duration::from_secs(30)
};
// 1. Fetch Torrents // 1. Fetch Torrents
let torrents_result = sse::fetch_torrents(&client).await; let torrents_result = sse::fetch_torrents(&client).await;
@@ -429,6 +442,14 @@ async fn main() {
} }
previous_torrents = new_torrents; previous_torrents = new_torrents;
// Success case: wait for the determined interval OR a wakeup notification
tokio::select! {
_ = tokio::time::sleep(loop_interval) => {},
_ = notify_poll_clone.notified() => {
tracing::debug!("Background loop awakened by new client connection");
}
}
} }
Err(e) => { Err(e) => {
tracing::error!("Error fetching torrents in background: {}", e); tracing::error!("Error fetching torrents in background: {}", e);
@@ -449,20 +470,15 @@ async fn main() {
"Backoff: Sleeping for {:?} due to rTorrent error.", "Backoff: Sleeping for {:?} due to rTorrent error.",
backoff_duration backoff_duration
); );
tokio::time::sleep(backoff_duration).await;
} }
} }
// Handle Stats // Handle Stats
match stats_result { if let Ok(stats) = stats_result {
Ok(stats) => { let _ = event_bus_tx.send(AppEvent::Stats(stats));
let _ = event_bus_tx.send(AppEvent::Stats(stats));
}
Err(e) => {
tracing::warn!("Error fetching global stats: {}", e);
}
} }
tokio::time::sleep(backoff_duration).await;
} }
}); });

View File

@@ -195,6 +195,9 @@ pub async fn fetch_global_stats(client: &RtorrentClient) -> Result<GlobalStats,
pub async fn sse_handler( pub async fn sse_handler(
State(state): State<AppState>, State(state): State<AppState>,
) -> Sse<impl Stream<Item = Result<Event, Infallible>>> { ) -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
// Notify background worker to wake up and poll immediately
state.notify_poll.notify_one();
// Get initial value synchronously (from the watch channel's current state) // Get initial value synchronously (from the watch channel's current state)
let initial_rx = state.tx.subscribe(); let initial_rx = state.tx.subscribe();
let initial_torrents = initial_rx.borrow().clone(); let initial_torrents = initial_rx.borrow().clone();

View File

@@ -254,7 +254,7 @@ pub fn TorrentTable() -> impl IntoView {
<div class="flex items-center">"Status" {move || sort_arrow(SortColumn::Status)}</div> <div class="flex items-center">"Status" {move || sort_arrow(SortColumn::Status)}</div>
</th> </th>
<th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::DownSpeed)> <th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::DownSpeed)>
<div class="flex items-center">"Down Speed" {move || sort_arrow(SortColumn::DownSpeed)}</div> <div class="flex items-center">"DL Speed" {move || sort_arrow(SortColumn::DownSpeed)}</div>
</th> </th>
<th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::UpSpeed)> <th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::UpSpeed)>
<div class="flex items-center">"Up Speed" {move || sort_arrow(SortColumn::UpSpeed)}</div> <div class="flex items-center">"Up Speed" {move || sort_arrow(SortColumn::UpSpeed)}</div>
@@ -345,7 +345,7 @@ pub fn TorrentTable() -> impl IntoView {
(SortColumn::Size, "Size"), (SortColumn::Size, "Size"),
(SortColumn::Progress, "Progress"), (SortColumn::Progress, "Progress"),
(SortColumn::Status, "Status"), (SortColumn::Status, "Status"),
(SortColumn::DownSpeed, "Down Speed"), (SortColumn::DownSpeed, "DL Speed"),
(SortColumn::UpSpeed, "Up Speed"), (SortColumn::UpSpeed, "Up Speed"),
(SortColumn::ETA, "ETA"), (SortColumn::ETA, "ETA"),
(SortColumn::AddedDate, "Date"), (SortColumn::AddedDate, "Date"),