fix(notification-service): resolve NotificationService-010,011,012 — disconnect SMTP on failure, relocate exception type, OAuth2/token-cache test coverage
This commit is contained in:
@@ -315,8 +315,6 @@ public class NotificationDeliveryService : INotificationDeliveryService
|
||||
|
||||
var bccAddresses = recipients.Select(r => r.EmailAddress).ToList();
|
||||
await smtp.SendAsync(config.FromAddress, bccAddresses, subject, body, cancellationToken);
|
||||
|
||||
await smtp.DisconnectAsync(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -334,6 +332,23 @@ public class NotificationDeliveryService : INotificationDeliveryService
|
||||
// catch filters (SmtpPermanentException / IsTransientSmtpError) handle them.
|
||||
finally
|
||||
{
|
||||
// NS-010: always tear the connection down, regardless of outcome. The
|
||||
// SMTP QUIT used to run only on the success path inside the try block,
|
||||
// so a failed Connect/Authenticate/Send left an open, authenticated
|
||||
// connection until finalization reclaimed the socket — exhausting the
|
||||
// server's connection slots under sustained transient failures.
|
||||
// Disconnect is best-effort: a disconnect failure (e.g. the connection
|
||||
// is already dead) must not mask the original delivery exception.
|
||||
try
|
||||
{
|
||||
await smtp.DisconnectAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception disconnectEx)
|
||||
{
|
||||
_logger.LogDebug(
|
||||
"Ignoring SMTP disconnect failure during cleanup: {Reason}", disconnectEx.Message);
|
||||
}
|
||||
|
||||
// NS-007: always release the concurrency slot, even on failure.
|
||||
limiter.Release();
|
||||
}
|
||||
@@ -399,12 +414,3 @@ public class NotificationDeliveryService : INotificationDeliveryService
|
||||
return ClassifySmtpError(ex, cancellationToken) == SmtpErrorClass.Transient;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Signals a permanent SMTP failure (5xx) that should not be retried.
|
||||
/// </summary>
|
||||
public class SmtpPermanentException : Exception
|
||||
{
|
||||
public SmtpPermanentException(string message, Exception? innerException = null)
|
||||
: base(message, innerException) { }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user