fix(deployment): bulk-deploy up-to-date pre-filter uses batch hash/status reads and the operation's shared flatten session; completion log reports pre-filter cost (review 2026-10-21 04/P1)

This commit is contained in:
Joseph Doherty
2026-10-22 06:46:03 -04:00
parent df67fabe68
commit 32e63eaa96
11 changed files with 247 additions and 78 deletions
@@ -190,6 +190,30 @@ public class DeploymentManagerRepository : IDeploymentManagerRepository
.FirstOrDefaultAsync(cancellationToken);
}
/// <inheritdoc />
public async Task<IReadOnlyDictionary<int, DeploymentRecord>> GetCurrentDeploymentStatusesAsync(
IReadOnlyCollection<int> instanceIds, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(instanceIds);
if (instanceIds.Count == 0)
return new Dictionary<int, DeploymentRecord>();
var ids = instanceIds.Distinct().ToArray();
// "Current" = no newer record for the same instance, with the same (DeployedAt, Id)
// tie-break GetCurrentDeploymentStatusAsync uses. Resolved in SQL so only one row per
// instance comes back however long each instance's deploy history is.
var records = await _dbContext.DeploymentRecords
.AsNoTracking()
.Where(d => ids.Contains(d.InstanceId))
.Where(d => !_dbContext.DeploymentRecords.Any(o =>
o.InstanceId == d.InstanceId
&& (o.DeployedAt > d.DeployedAt || (o.DeployedAt == d.DeployedAt && o.Id > d.Id))))
.ToListAsync(cancellationToken);
return records.ToDictionary(d => d.InstanceId);
}
/// <inheritdoc />
public async Task<DeploymentRecord?> GetDeploymentByDeploymentIdAsync(string deploymentId, CancellationToken cancellationToken = default)
{