Standard close: the loser of a simultaneous close adopts the winner's result
Status is a concurrency token, so the second SaveChanges of two simultaneous CloseMotionAsync calls throws DbUpdateConcurrencyException. Reload and return, with no second motion.closed audit event; the same pattern as the sealed closer. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
012d1cb926
commit
ee3a2f49f5
1 changed files with 15 additions and 1 deletions
|
|
@ -126,7 +126,21 @@ public sealed class MotionGovernanceService(
|
|||
? $"Closed with {votesFor:0.##} for and {votesAgainst:0.##} against at a {motion.ApprovalThreshold:P0} threshold."
|
||||
: "Closed without decisive votes.";
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException error)
|
||||
{
|
||||
// Status is a concurrency token: another closer won. Adopt its result and record nothing a second time.
|
||||
foreach (var entry in error.Entries)
|
||||
{
|
||||
await entry.ReloadAsync(cancellationToken);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await auditTrailService.RecordAsync(
|
||||
actorUser.Id,
|
||||
nameof(Motion),
|
||||
|
|
|
|||
Loading…
Reference in a new issue