fix: avoid feedback screenshot concurrency save
All checks were successful
deploy-socialize / image (push) Successful in 33s
deploy-socialize / deploy (push) Successful in 19s

This commit is contained in:
2026-05-06 20:14:22 -04:00
parent afe22949c5
commit 4eb0fbc22b

View File

@@ -38,8 +38,7 @@ public class AttachMyFeedbackScreenshotHandler(
Guid reporterUserId = User.GetUserId();
FeedbackReport? report = await dbContext.FeedbackReports
.Include(candidate => candidate.Tags)
.Include(candidate => candidate.Screenshot)
.AsNoTracking()
.SingleOrDefaultAsync(
candidate => candidate.Id == id && candidate.ReporterUserId == reporterUserId,
ct);
@@ -50,7 +49,11 @@ public class AttachMyFeedbackScreenshotHandler(
return;
}
if (report.Screenshot is not null)
bool hasScreenshot = await dbContext.FeedbackScreenshots
.AsNoTracking()
.AnyAsync(candidate => candidate.FeedbackReportId == report.Id, ct);
if (hasScreenshot)
{
AddError("A screenshot is already attached to this feedback report.");
await SendErrorsAsync(StatusCodes.Status409Conflict, ct);
@@ -94,7 +97,7 @@ public class AttachMyFeedbackScreenshotHandler(
}
DateTimeOffset now = DateTimeOffset.UtcNow;
report.Screenshot = new FeedbackScreenshot
FeedbackScreenshot screenshot = new()
{
Id = screenshotId,
FeedbackReportId = report.Id,
@@ -105,10 +108,45 @@ public class AttachMyFeedbackScreenshotHandler(
BlobName = blobName,
CreatedAt = now,
};
report.LastActivityAt = now;
await using Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction transaction =
await dbContext.Database.BeginTransactionAsync(ct);
dbContext.FeedbackScreenshots.Add(screenshot);
try
{
await dbContext.SaveChangesAsync(ct);
await SendOkAsync(report.ToDto(), ct);
}
catch (DbUpdateException)
{
await transaction.RollbackAsync(ct);
AddError("A screenshot is already attached to this feedback report.");
await SendErrorsAsync(StatusCodes.Status409Conflict, ct);
return;
}
int updatedRows = await dbContext.FeedbackReports
.Where(candidate => candidate.Id == report.Id && candidate.ReporterUserId == reporterUserId)
.ExecuteUpdateAsync(
setters => setters.SetProperty(candidate => candidate.LastActivityAt, now),
ct);
if (updatedRows == 0)
{
await transaction.RollbackAsync(ct);
await SendNotFoundAsync(ct);
return;
}
await transaction.CommitAsync(ct);
FeedbackReport responseReport = await dbContext.FeedbackReports
.AsNoTracking()
.Include(candidate => candidate.Tags)
.Include(candidate => candidate.Screenshot)
.SingleAsync(candidate => candidate.Id == report.Id, ct);
await SendOkAsync(responseReport.ToDto(), ct);
}
private static string NormalizeFileName(string? fileName, string extension)