Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/download_system/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,18 @@ impl Transfer {
let service_result = match app.check_imported(&target.to).await {
Ok(r) => r,
Err(e) => {
error!("Error retrieving history from {}: {}", app, e);
// A misconfigured/unreachable *arr fails for every
// transfer on every poll; throttle the log so it doesn't
// fill the disk over time (issue #21). Key on the app's
// existing name (no allocation on the suppressed path).
if self.app_data.state.should_log_arr_error(&app.name).await {
error!(
"Error retrieving history from {} (suppressing repeats for {:?}): {}",
app,
crate::state::StateManager::ARR_ERROR_LOG_INTERVAL,
e
);
}
false
}
};
Expand Down
24 changes: 24 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ pub struct StateManager {
/// 404); without this, every torrent-get would re-hit the API and re-log a
/// warning. Retries are suppressed until [`Self::NAME_FAILURE_TTL`] passes.
failed_names: Arc<RwLock<HashMap<i64, Instant>>>,
/// Last time a connection error was logged for each *arr, used to throttle
/// the log. A misconfigured Sonarr/Radarr fails on every poll for every
/// transfer, and logging each one filled users' disks over time (issue #21).
arr_error_logged: Arc<RwLock<HashMap<String, Instant>>>,
}

impl StateManager {
Expand All @@ -52,6 +56,26 @@ impl StateManager {
local_complete: Arc::new(RwLock::new(HashSet::new())),
file_names: Arc::new(RwLock::new(HashMap::new())),
failed_names: Arc::new(RwLock::new(HashMap::new())),
arr_error_logged: Arc::new(RwLock::new(HashMap::new())),
}
}

/// Minimum time between logging the same *arr's connection error.
pub const ARR_ERROR_LOG_INTERVAL: Duration = Duration::from_secs(300);

/// Returns true if an error for `app` should be logged now, throttling
/// repeats to at most one per [`Self::ARR_ERROR_LOG_INTERVAL`]. Keeps a
/// persistently unreachable/misconfigured *arr from filling the disk with
/// identical error lines on every poll (issue #21).
pub async fn should_log_arr_error(&self, app: &str) -> bool {
let mut map = self.arr_error_logged.write().await;
let now = Instant::now();
match map.get(app) {
Some(at) if now.saturating_duration_since(*at) < Self::ARR_ERROR_LOG_INTERVAL => false,
_ => {
map.insert(app.to_string(), now);
true
}
}
}

Expand Down