From 087f638b878017318efea39051958c3d0f130f27 Mon Sep 17 00:00:00 2001
From: Daniel Collingwood <82693586+danzuep@users.noreply.github.com>
Date: Thu, 8 Aug 2024 01:01:08 +0800
Subject: [PATCH] #72 added retry to arrival and departure queues
---
.../Extensions/EmailReceiverExtensions.cs | 18 +++--
.../Extensions/LoggerExtensions.cs | 25 +++++++
.../Services/MailFolderMonitor.cs | 75 +++++++++++--------
3 files changed, 79 insertions(+), 39 deletions(-)
create mode 100644 source/MailKitSimplified.Receiver/Extensions/LoggerExtensions.cs
diff --git a/source/MailKitSimplified.Receiver/Extensions/EmailReceiverExtensions.cs b/source/MailKitSimplified.Receiver/Extensions/EmailReceiverExtensions.cs
index 2951549..056ab72 100644
--- a/source/MailKitSimplified.Receiver/Extensions/EmailReceiverExtensions.cs
+++ b/source/MailKitSimplified.Receiver/Extensions/EmailReceiverExtensions.cs
@@ -1,10 +1,11 @@
-using MailKit;
-using MailKit.Search;
-using System;
-using System.Linq;
-using System.Threading;
+using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text.Json;
+using System.Threading;
+using MailKit;
+using MailKit.Search;
namespace MailKitSimplified.Receiver.Extensions
{
@@ -12,8 +13,11 @@ namespace MailKitSimplified.Receiver.Extensions
public static class EmailReceiverExtensions
{
///
- public static string ToEnumeratedString(this IEnumerable data, string div = ", ") =>
- data is null ? "" : string.Join(div, data.Select(o => o?.ToString() ?? ""));
+ public static string ToEnumeratedString(this IEnumerable data, string delimiter = ", ") =>
+ data is null ? string.Empty : string.Join(delimiter, data.Select(o => o?.ToString() ?? string.Empty));
+
+ public static string ToSerializedString(this object obj) =>
+ obj != null ? JsonSerializer.Serialize(obj) : string.Empty;
///
public static IList TryAddUniqueRange(this IList list, IEnumerable items) where T : IMessageSummary {
diff --git a/source/MailKitSimplified.Receiver/Extensions/LoggerExtensions.cs b/source/MailKitSimplified.Receiver/Extensions/LoggerExtensions.cs
new file mode 100644
index 0000000..6632ca8
--- /dev/null
+++ b/source/MailKitSimplified.Receiver/Extensions/LoggerExtensions.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Logging;
+
+namespace MailKitSimplified.Receiver.Extensions
+{
+ ///
+ ///
+ ///
+ [ExcludeFromCodeCoverage]
+ public static class LoggerExtensions
+ {
+ public static void Serialized(this ILogger logger, T obj, LogLevel logLevel = LogLevel.Information) where T : class =>
+ logger.Log(logLevel, "\"{Name}\": {JsonSerializedObject}", typeof(T).Name, obj.ToSerializedString());
+
+ internal static Action LogAction(string message, LogLevel logLevel, int id) =>
+ LoggerMessage.Define(logLevel, new EventId(id, name: typeof(T).Name), message);
+
+ public static void Log(this ILogger logger, string message, LogLevel logLevel = LogLevel.Information, int id = 0) =>
+ LogAction(message, logLevel, id)(logger, null);
+
+ public static void Log(this ILogger logger, Exception ex, string message, LogLevel logLevel = LogLevel.Error, int id = 1) =>
+ LogAction(message, logLevel, id)(logger, ex);
+ }
+}
\ No newline at end of file
diff --git a/source/MailKitSimplified.Receiver/Services/MailFolderMonitor.cs b/source/MailKitSimplified.Receiver/Services/MailFolderMonitor.cs
index cc699a5..726143c 100644
--- a/source/MailKitSimplified.Receiver/Services/MailFolderMonitor.cs
+++ b/source/MailKitSimplified.Receiver/Services/MailFolderMonitor.cs
@@ -318,10 +318,11 @@ private async ValueTask WaitForNewMessagesAsync(CancellationToken cancellationTo
}
catch (ImapProtocolException ex)
{
+ var message = $"{ex.Message} Reconnecting and trying again.";
if (ex.Message.StartsWith("Idle timeout"))
- _logger.LogDebug($"{ex.Message} Trying again.");
+ _logger.Log(message, LogLevel.Debug);
else
- _logger.LogInformation(ex, "IMAP protocol exception, checking connection.");
+ _logger.Log(message, LogLevel.Information);
await ReconnectAsync(cancellationToken).ConfigureAwait(false);
if (_folderMonitorOptions.IdleMinutes > FolderMonitorOptions.IdleMinutesGmail)
_folderMonitorOptions.IdleMinutes = FolderMonitorOptions.IdleMinutesGmail;
@@ -392,59 +393,69 @@ private async ValueTask ProcessMessagesArrivedAsync(bool firstConnection =
private async Task ProcessArrivalQueueAsync(Func messageArrivalMethod, CancellationToken cancellationToken = default)
{
- IMessageSummary messageSummary = null;
- try
+ int retryCount = 0;
+ if (messageArrivalMethod != null)
{
- if (messageArrivalMethod != null)
+ IMessageSummary messageSummary = null;
+ do
{
- do
+ retryCount++;
+ try
{
if (_arrivalQueue.TryDequeue(out messageSummary))
await messageArrivalMethod(messageSummary).ConfigureAwait(false);
else if (_arrivalQueue.IsEmpty)
await Task.Delay(_folderMonitorOptions.EmptyQueueMaxDelayMs, cancellationToken).ConfigureAwait(false);
+ retryCount = 0;
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogTrace("Arrival queue cancelled.");
+ break;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, $"Error occurred processing arrival queue item, backing off for {_folderMonitorOptions.EmptyQueueMaxDelayMs}ms. {_imapReceiver} #{messageSummary.UniqueId}.");
+ if (messageSummary != null)
+ _arrivalQueue.Enqueue(messageSummary);
+ await Task.Delay(_folderMonitorOptions.EmptyQueueMaxDelayMs, cancellationToken).ConfigureAwait(false);
}
- while (!cancellationToken.IsCancellationRequested);
}
- }
- catch (OperationCanceledException)
- {
- _logger.LogTrace("Arrival queue cancelled.");
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Error occurred processing arrival queue item. {_imapReceiver} #{messageSummary.UniqueId}.");
- if (messageSummary != null)
- _arrivalQueue.Enqueue(messageSummary);
+ while (!cancellationToken.IsCancellationRequested && retryCount < _folderMonitorOptions.MaxRetries);
}
}
private async Task ProcessDepartureQueueAsync(Func messageDepartureMethod, CancellationToken cancellationToken = default)
{
- IMessageSummary messageSummary = null;
- try
+ int retryCount = 0;
+ if (messageDepartureMethod != null)
{
- if (messageDepartureMethod != null)
+ IMessageSummary messageSummary = null;
+ do
{
- do
+ retryCount++;
+ try
{
if (_departureQueue.TryDequeue(out messageSummary))
await messageDepartureMethod(messageSummary).ConfigureAwait(false);
else if (_departureQueue.IsEmpty)
await Task.Delay(_folderMonitorOptions.EmptyQueueMaxDelayMs, cancellationToken).ConfigureAwait(false);
+ retryCount = 0;
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogTrace("Departure queue cancelled.");
+ break;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, $"Error occurred processing departure queue item, backing off for {_folderMonitorOptions.EmptyQueueMaxDelayMs}ms. {_imapReceiver} #{messageSummary.UniqueId}.");
+ if (messageSummary != null)
+ _departureQueue.Enqueue(messageSummary);
+ await Task.Delay(_folderMonitorOptions.EmptyQueueMaxDelayMs, cancellationToken).ConfigureAwait(false);
}
- while (!cancellationToken.IsCancellationRequested);
}
- }
- catch (OperationCanceledException)
- {
- _logger.LogTrace("Departure queue cancelled.");
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, $"Error occurred processing departure queue item. {_imapReceiver} #{messageSummary.UniqueId}.");
- if (messageSummary != null)
- _departureQueue.Enqueue(messageSummary);
+ while (!cancellationToken.IsCancellationRequested && retryCount < _folderMonitorOptions.MaxRetries);
}
}