Skip to content

Commit

Permalink
Fix memory leak at outbox email queueing (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-lerch authored Jun 10, 2024
1 parent 87b14fa commit d672900
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 11 deletions.
6 changes: 2 additions & 4 deletions server/Korga/EmailDelivery/EmailDeliveryJobController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ protected override async ValueTask ExecuteJob(OutboxEmail outboxEmail, Cancellat
{
SmtpClient smtp = await GetConnection(cancellationToken);

MimeMessage mimeMessage;

using (MemoryStream memoryStream = new(outboxEmail.Content))
mimeMessage = MimeMessage.Load(memoryStream, CancellationToken.None);
using MemoryStream memoryStream = new(outboxEmail.Content);
using MimeMessage mimeMessage = MimeMessage.Load(memoryStream, CancellationToken.None);

try
{
Expand Down
11 changes: 9 additions & 2 deletions server/Korga/EmailDelivery/EmailDeliveryService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Korga.Utilities;
using Korga.EmailDelivery.Entities;
using Korga.Utilities;
using Microsoft.EntityFrameworkCore;
using MimeKit;
using System;
Expand Down Expand Up @@ -35,8 +36,14 @@ public async ValueTask<bool> Enqueue(string emailAddress, MimeMessage mimeMessag
content = memoryStream.ToArray();
}

database.OutboxEmails.Add(new(emailAddress, content) { InboxEmailId = inboxEmailId });
OutboxEmail outboxEmail = new(emailAddress, content) { InboxEmailId = inboxEmailId };
database.OutboxEmails.Add(outboxEmail);
await database.SaveChangesAsync(cancellationToken);

// Explicitly free entity for garbage collection because our DbContext won't be disposed soon enough
// Without this line, Korga takes gigabytes of memory when sending large messages to many recipients
database.Entry(outboxEmail).State = EntityState.Detached;

jobQueue.EnsureRunning();
return true;
}
Expand Down
14 changes: 9 additions & 5 deletions server/Korga/EmailRelay/EmailRelayJobController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ protected override async ValueTask ExecuteJob(InboxEmail email, CancellationToke
{
if (email.Receiver == null)
{
await SendErrorMessage(email, emailRelay.InvalidServerConfiguration(email), cancellationToken);
using MimeMessage? errorMessage = emailRelay.InvalidServerConfiguration(email);
await SendErrorMessage(email, errorMessage, cancellationToken);

logger.LogWarning("Could not determine receiver for message #{Id} from {From} to {To}. This message will not be forwarded." +
"Please make sure your email provider specifies the receiver in the Received, Envelope-To, or X-Envelope-To header", email.Id, email.From, email.To);
Expand All @@ -54,23 +55,26 @@ protected override async ValueTask ExecuteJob(InboxEmail email, CancellationToke

if (distributionList == null)
{
await SendErrorMessage(email, emailRelay.InvalidAlias(email), cancellationToken);
using MimeMessage? errorMessage = emailRelay.InvalidAlias(email);
await SendErrorMessage(email, errorMessage, cancellationToken);

logger.LogInformation("No group found with alias {Receiver} for email #{Id} from {From}", email.Receiver, email.Id, email.From);
return;
}

if (email.Header == null)
{
await SendErrorMessage(email, emailRelay.TooManyHeaders(email), cancellationToken);
using MimeMessage? errorMessage = emailRelay.TooManyHeaders(email);
await SendErrorMessage(email, errorMessage, cancellationToken);

logger.LogInformation("Email #{Id} from {From} to {Receiver} exceeded the header size limit", email.Id, email.From, email.Receiver);
return;
}

if (email.Body == null)
{
await SendErrorMessage(email, emailRelay.TooBigMessage(email), cancellationToken);
using MimeMessage? errorMessage = emailRelay.TooBigMessage(email);
await SendErrorMessage(email, errorMessage, cancellationToken);

logger.LogInformation("Email #{Id} from {From} to {Receiver} exceeded the body size limit", email.Id, email.From, email.Receiver);
return;
Expand All @@ -79,7 +83,7 @@ protected override async ValueTask ExecuteJob(InboxEmail email, CancellationToke
MailboxAddress[] recipients = await distributionListService.GetRecipients(distributionList, cancellationToken);
foreach (MailboxAddress address in recipients)
{
MimeMessage preparedMessage = distributionList.Flags.HasFlag(DistributionListFlags.Newsletter)
using MimeMessage preparedMessage = distributionList.Flags.HasFlag(DistributionListFlags.Newsletter)
? await emailRelay.PrepareForForwardTo(email, address, cancellationToken)
: emailRelay.PrepareForResentTo(email, address);
await emailDelivery.Enqueue(address.Address, preparedMessage, email.Id, cancellationToken);
Expand Down

0 comments on commit d672900

Please sign in to comment.