perf(gmail): batch the duplicate-message check in CreateSuggestedJob
CreateSuggestedJob ran one AnyAsync per message in the thread to decide imported-vs-skip — an N+1 that scales with thread length. Replace it with a single query that loads the already-imported ExternalMessageIds for the job, then check in memory (identical skip/import behaviour), mirroring the batched pattern RelinkThread already uses. Build clean; backend suite 135/135 green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -641,12 +641,17 @@ public sealed class GmailController : ControllerBase
|
||||
|
||||
var threadMessages = await _gmail.ListThreadMessagesAsync(ownerUserId, request.ThreadId.Trim(), cancellationToken);
|
||||
var distinctMessageIds = threadMessages.Select(message => message.Id).Where(static id => !string.IsNullOrWhiteSpace(id)).Distinct(StringComparer.Ordinal).ToList();
|
||||
// Batch the "already imported?" check with a single query instead of one
|
||||
// AnyAsync per message (N+1), mirroring RelinkThread below.
|
||||
var existingMessageIds = await _db.Correspondences
|
||||
.Where(message => message.JobApplicationId == job.Id && message.ExternalMessageId != null && distinctMessageIds.Contains(message.ExternalMessageId))
|
||||
.Select(message => message.ExternalMessageId!)
|
||||
.ToListAsync(cancellationToken);
|
||||
var imported = 0;
|
||||
var skipped = 0;
|
||||
foreach (var messageId in distinctMessageIds)
|
||||
{
|
||||
var existing = await _db.Correspondences.AnyAsync(message => message.JobApplicationId == job.Id && message.ExternalMessageId == messageId, cancellationToken);
|
||||
if (existing)
|
||||
if (existingMessageIds.Contains(messageId, StringComparer.Ordinal))
|
||||
{
|
||||
skipped++;
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user