fix(cv): sync operation terminal state
CI and Deploy / test (pull_request) Successful in 5m12s
CI and Deploy / deploy (pull_request) Has been skipped

This commit is contained in:
cesnimda
2026-08-15 19:45:44 +02:00
parent 7185491a05
commit dbff0f8d49
6 changed files with 123 additions and 3 deletions
@@ -249,6 +249,7 @@ public sealed class UserOperationStore(JobTrackerContext db, TimeProvider timePr
cancellationToken);
if (affected == 1 && !canRetry)
{
await SynchronizeCvRunAsync(operation, OperationStatuses.Failed, message, now, cancellationToken);
db.UserNotifications.Add(CreateTerminalNotification(operation, OperationStatuses.Failed, now));
await db.SaveChangesAsync(cancellationToken);
}
@@ -272,6 +273,7 @@ public sealed class UserOperationStore(JobTrackerContext db, TimeProvider timePr
cancellationToken);
if (cancelled == 1)
{
await SynchronizeCvRunAsync(operation, OperationStatuses.Cancelled, "CV processing was cancelled.", now, cancellationToken);
db.UserNotifications.Add(CreateTerminalNotification(operation, OperationStatuses.Cancelled, now));
await db.SaveChangesAsync(cancellationToken);
if (transaction is not null) await transaction.CommitAsync(cancellationToken);
@@ -303,6 +305,7 @@ public sealed class UserOperationStore(JobTrackerContext db, TimeProvider timePr
cancellationToken);
if (affected == 1)
{
await SynchronizeCvRunAsync(operation, OperationStatuses.Cancelled, "CV processing was cancelled.", now, cancellationToken);
db.UserNotifications.Add(CreateTerminalNotification(operation, OperationStatuses.Cancelled, now));
await db.SaveChangesAsync(cancellationToken);
if (transaction is not null) await transaction.CommitAsync(cancellationToken);
@@ -332,6 +335,7 @@ public sealed class UserOperationStore(JobTrackerContext db, TimeProvider timePr
var existingNotification = await db.UserNotifications.FirstOrDefaultAsync(item => item.OperationId == operationId, cancellationToken);
if (existingNotification is not null) db.UserNotifications.Remove(existingNotification);
await db.SaveChangesAsync(cancellationToken);
await SynchronizeCvRunAsync(operation, OperationStatuses.Queued, null, UtcNow, cancellationToken);
if (transaction is not null) await transaction.CommitAsync(cancellationToken);
return true;
}
@@ -393,6 +397,7 @@ public sealed class UserOperationStore(JobTrackerContext db, TimeProvider timePr
.SetProperty(item => item.LeaseExpiresAtUtc, (DateTime?)null), cancellationToken);
if (affected == 1)
{
await SynchronizeCvRunAsync(operation, status, message, now, cancellationToken);
db.UserNotifications.Add(CreateTerminalNotification(operation, status, now));
await db.SaveChangesAsync(cancellationToken);
if (transaction is not null) await transaction.CommitAsync(cancellationToken);
@@ -405,6 +410,43 @@ public sealed class UserOperationStore(JobTrackerContext db, TimeProvider timePr
return await db.Database.BeginTransactionAsync(cancellationToken);
}
private Task<int> SynchronizeCvRunAsync(
UserOperation operation,
string operationStatus,
string? message,
DateTime now,
CancellationToken cancellationToken)
{
if (!string.Equals(operation.TaskType, CvProcessingQueue.TaskType, StringComparison.Ordinal)
|| !string.Equals(operation.SubjectType, CvProcessingQueue.SubjectType, StringComparison.Ordinal)
|| !int.TryParse(operation.SubjectId, System.Globalization.NumberStyles.None,
System.Globalization.CultureInfo.InvariantCulture, out var runId)
|| runId <= 0)
{
return Task.FromResult(0);
}
var runStatus = operationStatus switch
{
OperationStatuses.Cancelled => "cancelled",
OperationStatuses.Failed => "failed",
OperationStatuses.Queued => "queued",
_ => null,
};
if (runStatus is null) return Task.FromResult(0);
var completedAt = OperationStatuses.IsTerminal(operationStatus)
? new DateTimeOffset(DateTime.SpecifyKind(now, DateTimeKind.Utc))
: (DateTimeOffset?)null;
return db.CvExtractionRuns.IgnoreQueryFilters()
.Where(run => run.Id == runId && run.OwnerUserId == operation.OwnerUserId)
.ExecuteUpdateAsync(setters => setters
.SetProperty(run => run.Status, runStatus)
.SetProperty(run => run.ErrorMessage, message)
.SetProperty(run => run.CompletedAtUtc, completedAt),
cancellationToken);
}
private static UserNotification CreateTerminalNotification(UserOperation operation, string status, DateTime now)
{
var (kind, title, message) = status switch