Skip to content

Commit

Permalink
#72 added retry to arrival and departure queues
Browse files Browse the repository at this point in the history
  • Loading branch information
danzuep committed Aug 7, 2024
1 parent 2575e8b commit 087f638
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
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
{
[ExcludeFromCodeCoverage]
public static class EmailReceiverExtensions
{
/// <inheritdoc cref="string.Join"/>
public static string ToEnumeratedString<T>(this IEnumerable<T> data, string div = ", ") =>
data is null ? "" : string.Join(div, data.Select(o => o?.ToString() ?? ""));
public static string ToEnumeratedString<T>(this IEnumerable<T> 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;

/// <inheritdoc cref="List{T}.AddRange(IEnumerable{T})"/>
public static IList<T> TryAddUniqueRange<T>(this IList<T> list, IEnumerable<T> items) where T : IMessageSummary {
Expand Down
25 changes: 25 additions & 0 deletions source/MailKitSimplified.Receiver/Extensions/LoggerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;

namespace MailKitSimplified.Receiver.Extensions
{
/// <summary>
/// <see href="https://learn.microsoft.com/en-us/dotnet/core/extensions/high-performance-logging"/>
/// </summary>
[ExcludeFromCodeCoverage]
public static class LoggerExtensions
{
public static void Serialized<T>(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<ILogger, Exception> LogAction<T>(string message, LogLevel logLevel, int id) =>
LoggerMessage.Define(logLevel, new EventId(id, name: typeof(T).Name), message);

public static void Log<T>(this ILogger logger, string message, LogLevel logLevel = LogLevel.Information, int id = 0) =>
LogAction<T>(message, logLevel, id)(logger, null);

public static void Log<T>(this ILogger logger, Exception ex, string message, LogLevel logLevel = LogLevel.Error, int id = 1) =>
LogAction<T>(message, logLevel, id)(logger, ex);
}
}
75 changes: 43 additions & 32 deletions source/MailKitSimplified.Receiver/Services/MailFolderMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MailFolderMonitor>(message, LogLevel.Debug);
else
_logger.LogInformation(ex, "IMAP protocol exception, checking connection.");
_logger.Log<MailFolderMonitor>(message, LogLevel.Information);
await ReconnectAsync(cancellationToken).ConfigureAwait(false);
if (_folderMonitorOptions.IdleMinutes > FolderMonitorOptions.IdleMinutesGmail)
_folderMonitorOptions.IdleMinutes = FolderMonitorOptions.IdleMinutesGmail;
Expand Down Expand Up @@ -392,59 +393,69 @@ private async ValueTask<int> ProcessMessagesArrivedAsync(bool firstConnection =

private async Task ProcessArrivalQueueAsync(Func<IMessageSummary, Task> 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<IMessageSummary, Task> 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);
}
}

Expand Down

0 comments on commit 087f638

Please sign in to comment.