Improve IMAP reconnection and error handling in polling

Refactored PollInboxAsync to better handle IMAP connection drops and protocol errors by reconnecting and resuming processing as needed. Switched from using a using statement to explicit disposal in a finally block. Now logs and recovers from transient IMAP issues, and ensures proper cancellation handling by rethrowing OperationCanceledException. This increases service robustness and reliability.
This commit is contained in:
2026-04-12 14:23:16 +01:00
parent 387c0dc155
commit 1c00512661

View File

@@ -90,7 +90,9 @@ public sealed partial class InboxMonitorService : BackgroundService
private async Task PollInboxAsync(CancellationToken ct)
{
using var client = await _imapFactory.CreateConnectedClientAsync(_imap, ct);
var client = await _imapFactory.CreateConnectedClientAsync(_imap, ct);
try
{
var inbox = client.Inbox;
await inbox.OpenAsync(FolderAccess.ReadWrite, ct);
@@ -130,6 +132,10 @@ public sealed partial class InboxMonitorService : BackgroundService
{
await ProcessMessageAsync(inbox, uid, spamFolder, ct);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing UID={Uid}", uid.Id);
@@ -140,6 +146,17 @@ public sealed partial class InboxMonitorService : BackgroundService
Uid = uid.Id, AccountName = AccountName
});
_processedUids.Add(uid.Id);
// Reconnect if the server dropped the connection (e.g. unexpected response after MoveToAsync)
if (!client.IsConnected || ex is MailKit.Net.Imap.ImapProtocolException)
{
_logger.LogWarning("IMAP connection lost for {Account}, reconnecting", AccountName);
try { client.Dispose(); } catch { }
client = await _imapFactory.CreateConnectedClientAsync(_imap, ct);
inbox = client.Inbox;
await inbox.OpenAsync(FolderAccess.ReadWrite, ct);
spamFolder = await FindSpamFolderAsync(client, ct);
}
}
if (uid.Id > _lastSeenUid) _lastSeenUid = uid.Id;
@@ -147,6 +164,11 @@ public sealed partial class InboxMonitorService : BackgroundService
await client.DisconnectAsync(true, ct);
}
finally
{
client.Dispose();
}
}
private async Task ProcessMessageAsync(
IMailFolder inbox, UniqueId uid, IMailFolder? spamFolder, CancellationToken ct)