Added Smtp reconnect on connection loss
Some checks failed
Docker Release / build (push) Has been cancelled
GitHub Release / Generate Release (push) Has been cancelled

This commit is contained in:
Mia Rose Winter 2024-04-08 11:46:54 +02:00
parent 8563391584
commit 1193d76838
Signed by: miawinter
GPG key ID: 4B6F6A83178F595E
2 changed files with 32 additions and 5 deletions

View file

@ -4,7 +4,7 @@ public interface IEmailService : IAsyncDisposable {
ValueTask ConnectAsync(CancellationToken cancellation); ValueTask ConnectAsync(CancellationToken cancellation);
ValueTask DisconnectAsync(CancellationToken cancellation); ValueTask DisconnectAsync(CancellationToken cancellation);
ValueTask SendEmailAsync(IEmail email); ValueTask SendEmailAsync(IEmail email, CancellationToken cancellation = default);
} }
public sealed class NoOpEmailService : IEmailService { public sealed class NoOpEmailService : IEmailService {
@ -12,5 +12,5 @@ public sealed class NoOpEmailService : IEmailService {
public ValueTask ConnectAsync(CancellationToken cancellation) => ValueTask.CompletedTask; public ValueTask ConnectAsync(CancellationToken cancellation) => ValueTask.CompletedTask;
public ValueTask DisconnectAsync(CancellationToken cancellation) => ValueTask.CompletedTask; public ValueTask DisconnectAsync(CancellationToken cancellation) => ValueTask.CompletedTask;
public ValueTask SendEmailAsync(IEmail email) => ValueTask.CompletedTask; public ValueTask SendEmailAsync(IEmail email, CancellationToken cancellation = default) => ValueTask.CompletedTask;
} }

View file

@ -1,4 +1,5 @@
using MailKit.Net.Smtp; using MailKit;
using MailKit.Net.Smtp;
using MailKit.Security; using MailKit.Security;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MimeKit; using MimeKit;
@ -43,7 +44,7 @@ public class SmtpEmailService(ILogger<SmtpEmailService> logger, IOptions<EmailCo
Client = null; Client = null;
} }
public async ValueTask SendEmailAsync(IEmail email) { public async ValueTask SendEmailAsync(IEmail email, CancellationToken cancellation = default) {
if (Client is null) throw new ApplicationException("Not connected."); if (Client is null) throw new ApplicationException("Not connected.");
var message = new MimeMessage { var message = new MimeMessage {
@ -66,10 +67,13 @@ public class SmtpEmailService(ILogger<SmtpEmailService> logger, IOptions<EmailCo
int retryCount = 0; int retryCount = 0;
while (retryCount < 3) { while (retryCount < 3) {
try { try {
await Client.SendAsync(message); await Client.SendAsync(message, cancellation);
Logger.LogInformation("Successfully send mail to {email} (subject: {subject}).", Logger.LogInformation("Successfully send mail to {email} (subject: {subject}).",
email.ReceiverEmail, email.Subject); email.ReceiverEmail, email.Subject);
return; return;
} catch (ServiceNotConnectedException ex) {
Logger.LogWarning(ex, "Not connected, attempting reconnect.");
if (!await TryReconnect(cancellation)) throw;
} catch (Exception ex) { } catch (Exception ex) {
retryCount++; retryCount++;
Logger.LogWarning(ex, "Error sending E-Mail to {email}. Try: {RetryCount}.", Logger.LogWarning(ex, "Error sending E-Mail to {email}. Try: {RetryCount}.",
@ -80,4 +84,27 @@ public class SmtpEmailService(ILogger<SmtpEmailService> logger, IOptions<EmailCo
// throw new EmailNotSendException(); // throw new EmailNotSendException();
Logger.LogError("Giving up"); Logger.LogError("Giving up");
} }
private async ValueTask<bool> TryReconnect(CancellationToken cancellation) {
int reconnectCount = 0;
while (true) {
try {
await DisconnectAsync(cancellation);
await ConnectAsync(cancellation);
return true;
} catch (Exception ex1) {
reconnectCount++;
// 2^11 = 2048 seconds ~= 34 minutes
if (reconnectCount <= 11) {
// 2 4 6 8 16 32 64... seconds
int waitTime = (int)Math.Pow(2, reconnectCount);
Logger.LogError(ex1, "Reconnect failed, Try: {ReconnectTry}. Will wait {ReconnectWaitTime} Seconds for next attempt.", reconnectCount, waitTime);
await Task.Delay(TimeSpan.FromSeconds(waitTime), cancellation);
} else {
Logger.LogCritical(ex1, "Reconnect retry count exceeded, giving up.");
return false;
}
}
}
}
} }