Refactor IMAP sync caching and improve thread safety
Replaced `IMemoryCache` with a thread-safe `ConcurrentDictionary` for managing IMAP sync dates in `LimilabsImapEmailService`. Added `GetLastImapSyncDate` and `SetLastImapSyncDate` methods to handle cache operations. Updated the `IImapEmailService` interface to include `GetLastImapSyncDate`. Simplified the `MarkAsSeenAsync` method signature for better readability. Introduced a private record type `ImapCacheKey` to encapsulate cache keys.
This commit is contained in:
@@ -29,5 +29,13 @@ public interface IImapEmailService
|
|||||||
long uid,
|
long uid,
|
||||||
string folder = "INBOX",
|
string folder = "INBOX",
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the last IMAP sync date for the specified account and folder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="accountId"></param>
|
||||||
|
/// <param name="folder"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
DateTime? GetLastImapSyncDate(int accountId, string folder = "INBOX");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -5,8 +5,8 @@ using DigitalData.MessagingService.Domain.Entities;
|
|||||||
using DigitalData.MessagingService.Infrastructure.Services.Extensions;
|
using DigitalData.MessagingService.Infrastructure.Services.Extensions;
|
||||||
using Limilabs.Client.IMAP;
|
using Limilabs.Client.IMAP;
|
||||||
using Limilabs.Mail;
|
using Limilabs.Mail;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace DigitalData.MessagingService.Infrastructure.Services;
|
namespace DigitalData.MessagingService.Infrastructure.Services;
|
||||||
@@ -15,7 +15,7 @@ namespace DigitalData.MessagingService.Infrastructure.Services;
|
|||||||
/// IMAP email service using Limilabs Mail.dll.
|
/// IMAP email service using Limilabs Mail.dll.
|
||||||
/// Opens a fresh connection per call — stateless and thread-safe.
|
/// Opens a fresh connection per call — stateless and thread-safe.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger, IRepository<ReceivedEmail> Repository, IMemoryCache Cache) : IImapEmailService
|
public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger, IRepository<ReceivedEmail> Repository) : IImapEmailService
|
||||||
{
|
{
|
||||||
private static readonly string CacheKeyPrefix = Guid.NewGuid().ToString();
|
private static readonly string CacheKeyPrefix = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger,
|
|||||||
// Server-side: only date range; all other filters are applied in-process after cache retrieval
|
// Server-side: only date range; all other filters are applied in-process after cache retrieval
|
||||||
List<ICriterion> criterions = [];
|
List<ICriterion> criterions = [];
|
||||||
|
|
||||||
var since = Cache.GetLastImapSyncDate(account.Id, folder);
|
var since = GetLastImapSyncDate(account.Id, folder);
|
||||||
|
|
||||||
if (since is not null && since != default)
|
if (since is not null && since != default)
|
||||||
criterions.Add(Expression.SentSince(since.Value));
|
criterions.Add(Expression.SentSince(since.Value));
|
||||||
@@ -112,7 +112,7 @@ public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger,
|
|||||||
|
|
||||||
emails.Add(email);
|
emails.Add(email);
|
||||||
|
|
||||||
Cache.SetLastImapSyncDate(account.Id, folder, operationStartTime);
|
SetLastImapSyncDate(account.Id, folder, operationStartTime);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -136,11 +136,7 @@ public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MarkAsSeenAsync(
|
public async Task MarkAsSeenAsync(EmailAccount account, long uid, string folder = "INBOX", CancellationToken cancel = default)
|
||||||
EmailAccount account,
|
|
||||||
long uid,
|
|
||||||
string folder = "INBOX",
|
|
||||||
CancellationToken cancel = default)
|
|
||||||
{
|
{
|
||||||
using var imap = await OpenAsync(account, folder, cancel);
|
using var imap = await OpenAsync(account, folder, cancel);
|
||||||
try
|
try
|
||||||
@@ -173,4 +169,21 @@ public class LimilabsImapEmailService(ILogger<LimilabsImapEmailService> Logger,
|
|||||||
|
|
||||||
return imap;
|
return imap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region IMAP Last Sync Date Cache
|
||||||
|
private readonly ConcurrentDictionary<ImapCacheKey, DateTime> _cache = new();
|
||||||
|
|
||||||
|
private record ImapCacheKey(int AccountId, string Folder);
|
||||||
|
|
||||||
|
public DateTime? GetLastImapSyncDate(int accountId, string folder = "INBOX")
|
||||||
|
{
|
||||||
|
return _cache.GetValueOrDefault(new ImapCacheKey(accountId, folder));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetLastImapSyncDate(int accountId, string folder, DateTime date)
|
||||||
|
{
|
||||||
|
var key = new ImapCacheKey(accountId, folder);
|
||||||
|
_cache[key] = date;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user