-
Notifications
You must be signed in to change notification settings - Fork 645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve email sending #6154
Improve email sending #6154
Changes from 2 commits
0c9f4f6
9ff2af9
53bf118
7a0b0a2
b6df662
3b07214
b59b620
fa70ea6
7918915
164856e
c7d9382
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Net.Mail; | ||
using System.Threading.Tasks; | ||
using AnglicanGeek.MarkdownMailer; | ||
using NuGetGallery.Configuration; | ||
|
||
namespace NuGetGallery.Services | ||
{ | ||
public class BackgroundMessageService : MessageService | ||
{ | ||
public BackgroundMessageService(IMailSender mailSender, IAppConfiguration config, ITelemetryClient telemetryClient) | ||
:base(mailSender, config, telemetryClient) | ||
{ | ||
} | ||
|
||
protected override void SendMessage(MailMessage mailMessage) | ||
{ | ||
// Send email as background task, as we don't want to delay the HTTP response. | ||
// Particularly when sending email fails and needs to be retried with a delay. | ||
Task.Run(() => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a concerth that we might run into thread pool depletion if we try to send a lot of email while SMTP server is unresponsive and causes retries. |
||
{ | ||
try | ||
{ | ||
base.SendMessage(mailMessage); | ||
} | ||
catch (Exception ex) | ||
{ | ||
// Log but swallow the exception. | ||
QuietLog.LogHandledException(ex); | ||
} | ||
}); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,12 @@ | |
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Net.Mail; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using System.Web; | ||
using AnglicanGeek.MarkdownMailer; | ||
using NuGetGallery.Configuration; | ||
|
@@ -16,15 +18,16 @@ namespace NuGetGallery | |
{ | ||
public class MessageService : CoreMessageService, IMessageService | ||
{ | ||
protected MessageService() | ||
{ | ||
} | ||
|
||
public MessageService(IMailSender mailSender, IAppConfiguration config) | ||
public MessageService(IMailSender mailSender, IAppConfiguration config, ITelemetryClient telemetryClient) | ||
: base(mailSender, config) | ||
{ | ||
this.telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient)); | ||
smtpUri = config.SmtpUri?.Host ?? throw new ArgumentNullException(nameof(config) + "." + nameof(config.SmtpUri)); | ||
} | ||
|
||
private readonly ITelemetryClient telemetryClient; | ||
private readonly string smtpUri; | ||
|
||
public IAppConfiguration Config | ||
{ | ||
get { return (IAppConfiguration)CoreConfiguration; } | ||
|
@@ -168,7 +171,11 @@ [change your email notification settings]({7}). | |
|
||
if (mailMessage.To.Any()) | ||
{ | ||
SendMessage(mailMessage, copySender); | ||
SendMessage(mailMessage); | ||
if (copySender) | ||
{ | ||
SendMessageToSender(mailMessage); | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -1014,21 +1021,20 @@ private bool AddAddressesForAccountManagementToEmail(MailMessage mailMessage, Us | |
return AddAddressesWithPermissionToEmail(mailMessage, user, ActionsRequiringPermissions.ManageAccount); | ||
} | ||
|
||
protected override void SendMessage(MailMessage mailMessage, bool copySender) | ||
protected override void AttemptSendMessage(MailMessage mailMessage) | ||
{ | ||
bool success = false; | ||
DateTimeOffset now = DateTimeOffset.UtcNow; | ||
Stopwatch sw = Stopwatch.StartNew(); | ||
try | ||
{ | ||
base.SendMessage(mailMessage, copySender); | ||
} | ||
catch (InvalidOperationException ex) | ||
{ | ||
// Log but swallow the exception | ||
QuietLog.LogHandledException(ex); | ||
base.AttemptSendMessage(mailMessage); | ||
success = true; | ||
} | ||
catch (SmtpException ex) | ||
finally | ||
{ | ||
// Log but swallow the exception | ||
QuietLog.LogHandledException(ex); | ||
sw.Stop(); | ||
telemetryClient.TrackDependency("SMTP", smtpUri, "SendMessage", null, now, sw.Elapsed, null, success); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pattern we use for this kind of thing is a Also, this out of scope for this PR, but we really should merge the Gallery's and ServerCommon's telemetry APIs... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it may be worth tracking whether the operation was a success or not so that we measure how good/bad our mail system is. In reply to: 201454393 [](ancestors = 201454393) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to use TrackDependency for a few reasons.
I'm happy to change it to a metric, but those are the reasons I added There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are very good points! It sounds like |
||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably need to track the amount of retries we do.