fix(cv-qa): protect import lifecycle races and terminal receipts

This commit is contained in:
cesnimda
2026-10-27 00:27:57 +01:00
parent cf0e5c23de
commit 89974a257e
8 changed files with 250 additions and 11 deletions
@@ -53,15 +53,25 @@ public sealed class CvImportRetentionHostedService(IServiceScopeFactory scopes,
catch (IOException) { continue; }
catch (UnauthorizedAccessException) { continue; }
artifact.StoragePath = "";
run.DocumentJson = null;
run.RawExtractedText = null;
run.NormalizedText = null;
if (run.Status != "applied")
for (var attempt = 0; ; attempt++)
{
run.StructuredProfileJson = null;
if (run.Status is "pending_review" or "queued" or "running") run.Status = "expired";
run.CompletedAtUtc ??= now;
run.ErrorMessage = "The source document expired. Upload it again to start a new review.";
run.DocumentJson = null;
run.RawExtractedText = null;
run.NormalizedText = null;
if (run.Status != "applied")
{
run.StructuredProfileJson = null;
if (run.Status is "pending_review" or "queued" or "running") run.Status = "expired";
run.CompletedAtUtc ??= now;
run.ErrorMessage = "The source document expired. Upload it again to start a new review.";
}
try { await db.SaveChangesAsync(token); break; }
catch (DbUpdateConcurrencyException ex) when (attempt < 2 && ex.Entries.All(e => ReferenceEquals(e.Entity, run)))
{
// Retry source cleanup against the new status; never replay canonical profile writes.
await db.Entry(run).ReloadAsync(token);
if (db.Entry(run).State == EntityState.Detached) break;
}
}
removed++;
}