mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-30 01:44:23 +00:00
fix(imap): downgrade empty tail-fetch from error to info #342
This commit is contained in:
@@ -151,13 +151,7 @@ impl ImapExecutor {
|
|||||||
match before {
|
match before {
|
||||||
Some(date) => {
|
Some(date) => {
|
||||||
Self::fetch_new_mail_with_before(
|
Self::fetch_new_mail_with_before(
|
||||||
session,
|
session, account, mailbox, start_uid, date, examined, token,
|
||||||
account,
|
|
||||||
mailbox,
|
|
||||||
start_uid,
|
|
||||||
date,
|
|
||||||
examined,
|
|
||||||
token,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -226,7 +220,7 @@ impl ImapExecutor {
|
|||||||
start_uid,
|
start_uid,
|
||||||
examined.uid_next,
|
examined.uid_next,
|
||||||
) {
|
) {
|
||||||
tracing::warn!(
|
tracing::info!(
|
||||||
account_id = account.id,
|
account_id = account.id,
|
||||||
mailbox = %mailbox.name,
|
mailbox = %mailbox.name,
|
||||||
start_uid,
|
start_uid,
|
||||||
@@ -234,14 +228,14 @@ impl ImapExecutor {
|
|||||||
"{}",
|
"{}",
|
||||||
msg
|
msg
|
||||||
);
|
);
|
||||||
DownloadState::append_session_error(account.id, msg)?;
|
//DownloadState::append_session_error(account.id, msg)?;
|
||||||
DownloadState::update_folder_progress(
|
DownloadState::update_folder_progress(
|
||||||
account.id,
|
account.id,
|
||||||
mailbox.name.clone(),
|
mailbox.name.clone(),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
FolderStatus::Failed,
|
FolderStatus::Success,
|
||||||
Some("UID enumeration came back empty despite new mail on server. Retrying on next sync.".into()),
|
Some("UID FETCH range returned empty; tail UIDs appear to have been purged. highest_uid unchanged, will retry on next sync.".into()),
|
||||||
)?;
|
)?;
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
@@ -373,15 +367,11 @@ impl ImapExecutor {
|
|||||||
})?;
|
})?;
|
||||||
let mut entries: Vec<(u32, u64, i64)> = Vec::new();
|
let mut entries: Vec<(u32, u64, i64)> = Vec::new();
|
||||||
let mut skipped_oversized = 0u64;
|
let mut skipped_oversized = 0u64;
|
||||||
while let Some(fetch) = uid_stream
|
while let Some(fetch) = uid_stream.try_next().await.map_err(|e| {
|
||||||
.try_next()
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
let err_msg = format!("UID FETCH stream failed in [{}]: {:#?}", mailbox.name, e);
|
let err_msg = format!("UID FETCH stream failed in [{}]: {:#?}", mailbox.name, e);
|
||||||
let _ = DownloadState::append_session_error(account_id, err_msg);
|
let _ = DownloadState::append_session_error(account_id, err_msg);
|
||||||
raise_error!(format!("{:#?}", e), classify_imap_error(&e))
|
raise_error!(format!("{:#?}", e), classify_imap_error(&e))
|
||||||
})?
|
})? {
|
||||||
{
|
|
||||||
if token.is_cancelled() {
|
if token.is_cancelled() {
|
||||||
DownloadState::update_session_status(
|
DownloadState::update_session_status(
|
||||||
account_id,
|
account_id,
|
||||||
@@ -499,7 +489,7 @@ impl ImapExecutor {
|
|||||||
start_uid,
|
start_uid,
|
||||||
examined.uid_next,
|
examined.uid_next,
|
||||||
) {
|
) {
|
||||||
tracing::warn!(
|
tracing::info!(
|
||||||
account_id = account.id,
|
account_id = account.id,
|
||||||
mailbox = %mailbox.name,
|
mailbox = %mailbox.name,
|
||||||
start_uid,
|
start_uid,
|
||||||
@@ -507,14 +497,17 @@ impl ImapExecutor {
|
|||||||
"{}",
|
"{}",
|
||||||
msg
|
msg
|
||||||
);
|
);
|
||||||
DownloadState::append_session_error(account.id, msg)?;
|
//The gap between highest_uid and uid_next may be caused by messages being deleted on the server side.
|
||||||
|
//Reporting this as a warning/error makes users think that something is wrong with the sync.
|
||||||
|
|
||||||
|
//DownloadState::append_session_error(account.id, msg)?;
|
||||||
DownloadState::update_folder_progress(
|
DownloadState::update_folder_progress(
|
||||||
account.id,
|
account.id,
|
||||||
mailbox.name.clone(),
|
mailbox.name.clone(),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
FolderStatus::Failed,
|
FolderStatus::Success,
|
||||||
Some("UID enumeration came back empty despite new mail on server. Retrying on next sync.".into()),
|
Some("UID FETCH range returned empty; tail UIDs appear to have been purged. highest_uid unchanged, will retry on next sync.".into()),
|
||||||
)?;
|
)?;
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
@@ -760,8 +753,8 @@ impl ImapExecutor {
|
|||||||
// On error, report the number of emails already stored through the
|
// On error, report the number of emails already stored through the
|
||||||
// progress callback so a partial batch is not counted as fully
|
// progress callback so a partial batch is not counted as fully
|
||||||
// failed by callers (gap_fill uses the last reported count).
|
// failed by callers (gap_fill uses the last reported count).
|
||||||
let item = match tokio::time::timeout(STALL_REPORT_INTERVAL, body_stream.try_next()).await
|
let item =
|
||||||
{
|
match tokio::time::timeout(STALL_REPORT_INTERVAL, body_stream.try_next()).await {
|
||||||
Ok(Ok(item)) => Ok(item),
|
Ok(Ok(item)) => Ok(item),
|
||||||
Ok(Err(e)) => Err((count, e)),
|
Ok(Err(e)) => Err((count, e)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@@ -789,10 +782,7 @@ impl ImapExecutor {
|
|||||||
if let Some(progress) = progress {
|
if let Some(progress) = progress {
|
||||||
let _ = progress(processed, None, None);
|
let _ = progress(processed, None, None);
|
||||||
}
|
}
|
||||||
return Err(raise_error!(
|
return Err(raise_error!(format!("{:#?}", e), classify_imap_error(&e)));
|
||||||
format!("{:#?}", e),
|
|
||||||
classify_imap_error(&e)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let Some(fetch) = item else { break };
|
let Some(fetch) = item else { break };
|
||||||
@@ -944,12 +934,13 @@ impl ImapExecutor {
|
|||||||
session: &mut Session<Box<dyn SessionStream>>,
|
session: &mut Session<Box<dyn SessionStream>>,
|
||||||
uid_set: &str,
|
uid_set: &str,
|
||||||
token: CancellationToken,
|
token: CancellationToken,
|
||||||
progress: Option<
|
progress: Option<&(dyn Fn(u64, Option<f64>) -> BichonResult<()> + Send + Sync)>,
|
||||||
&(dyn Fn(u64, Option<f64>) -> BichonResult<()> + Send + Sync),
|
|
||||||
>,
|
|
||||||
) -> BichonResult<Vec<crate::archive::imap::download::gap_fill::RemoteHeader>> {
|
) -> BichonResult<Vec<crate::archive::imap::download::gap_fill::RemoteHeader>> {
|
||||||
let mut stream = session
|
let mut stream = session
|
||||||
.uid_fetch(uid_set, "(UID RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (MESSAGE-ID)])")
|
.uid_fetch(
|
||||||
|
uid_set,
|
||||||
|
"(UID RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (MESSAGE-ID)])",
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| raise_error!(format!("{:#?}", e), classify_imap_error(&e)))?;
|
.map_err(|e| raise_error!(format!("{:#?}", e), classify_imap_error(&e)))?;
|
||||||
|
|
||||||
@@ -959,13 +950,9 @@ impl ImapExecutor {
|
|||||||
// seconds so the UI shows the wait climbing instead of a stale state.
|
// seconds so the UI shows the wait climbing instead of a stale state.
|
||||||
const STALL_REPORT_INTERVAL: std::time::Duration = std::time::Duration::from_secs(5);
|
const STALL_REPORT_INTERVAL: std::time::Duration = std::time::Duration::from_secs(5);
|
||||||
loop {
|
loop {
|
||||||
let item =
|
let item = match tokio::time::timeout(STALL_REPORT_INTERVAL, stream.try_next()).await {
|
||||||
match tokio::time::timeout(STALL_REPORT_INTERVAL, stream.try_next()).await {
|
|
||||||
Ok(Ok(item)) => Ok(item),
|
Ok(Ok(item)) => Ok(item),
|
||||||
Ok(Err(e)) => Err(raise_error!(
|
Ok(Err(e)) => Err(raise_error!(format!("{:#?}", e), classify_imap_error(&e))),
|
||||||
format!("{:#?}", e),
|
|
||||||
classify_imap_error(&e)
|
|
||||||
)),
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
if token.is_cancelled() {
|
if token.is_cancelled() {
|
||||||
return Err(raise_error!(
|
return Err(raise_error!(
|
||||||
@@ -1126,10 +1113,7 @@ pub fn generate_uid_sequence_hashset(
|
|||||||
/// Returns the matching UIDs sorted ascending. Errors if the date cannot be
|
/// Returns the matching UIDs sorted ascending. Errors if the date cannot be
|
||||||
/// parsed — a silently-broken date filter would download mail the user asked
|
/// parsed — a silently-broken date filter would download mail the user asked
|
||||||
/// to exclude.
|
/// to exclude.
|
||||||
fn filter_before_date(
|
fn filter_before_date(entries: &[(u32, u64, i64)], date: &str) -> BichonResult<Vec<u32>> {
|
||||||
entries: &[(u32, u64, i64)],
|
|
||||||
date: &str,
|
|
||||||
) -> BichonResult<Vec<u32>> {
|
|
||||||
let cutoff = chrono::NaiveDate::parse_from_str(date, "%d-%b-%Y").map_err(|e| {
|
let cutoff = chrono::NaiveDate::parse_from_str(date, "%d-%b-%Y").map_err(|e| {
|
||||||
raise_error!(
|
raise_error!(
|
||||||
format!("Invalid BEFORE date '{date}': {e}"),
|
format!("Invalid BEFORE date '{date}': {e}"),
|
||||||
@@ -1277,10 +1261,7 @@ mod test {
|
|||||||
fn filter_before_date_timezone_ignored() {
|
fn filter_before_date_timezone_ignored() {
|
||||||
// Internal date late in the day in +1400 still counts as that day:
|
// Internal date late in the day in +1400 still counts as that day:
|
||||||
// 2025-05-26 23:59:00 +1400 == 2025-05-26 09:59 UTC.
|
// 2025-05-26 23:59:00 +1400 == 2025-05-26 09:59 UTC.
|
||||||
let entries = vec![
|
let entries = vec![(1, 100, ms(2025, 5, 25)), (2, 200, ms(2025, 5, 26))];
|
||||||
(1, 100, ms(2025, 5, 25)),
|
|
||||||
(2, 200, ms(2025, 5, 26)),
|
|
||||||
];
|
|
||||||
let uids = filter_before_date(&entries, "26-May-2025").unwrap();
|
let uids = filter_before_date(&entries, "26-May-2025").unwrap();
|
||||||
assert_eq!(uids, vec![1]);
|
assert_eq!(uids, vec![1]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user