feat(domain): timeout worker skips an already-resolved registration (refs #102)
Expire only a still-open (INGEDIEND/IN_BEHANDELING) registration; an already resolved one (expired, or withdrawn/decided while it waited) is left untouched so the job completes without violating the aggregate invariant (§8.6). Closes the S-10a/S-11 race where a withdrawal-while-waiting would loop the expiry job. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,9 +12,11 @@ namespace Big.Application;
|
|||||||
public sealed class ExpireRegistrationWorker(IRegistrationStore store)
|
public sealed class ExpireRegistrationWorker(IRegistrationStore store)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Process the job. Idempotent: a redelivered job whose registration is already VERLOPEN is a
|
/// Process the job. Idempotent and tolerant of races (§8.6, at-least-once delivery): a job whose
|
||||||
/// no-op — not persisted again (§8.6, at-least-once delivery). An unknown registration is an error:
|
/// registration is already resolved — a redelivered expiry (VERLOPEN), or one withdrawn/decided
|
||||||
/// it throws, leaving the job un-completed for Flowable to redeliver.
|
/// while it waited (INGETROKKEN/INGESCHREVEN/AFGEWEZEN) — is a no-op, so the job still completes
|
||||||
|
/// rather than throwing into a redelivery loop. Only a still-open registration is expired. An
|
||||||
|
/// unknown registration is an error: it throws, leaving the job un-completed for Flowable to redeliver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task HandleAsync(RegistratieVerlopenJob job, CancellationToken ct = default)
|
public async Task HandleAsync(RegistratieVerlopenJob job, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
@@ -24,8 +26,9 @@ public sealed class ExpireRegistrationWorker(IRegistrationStore store)
|
|||||||
?? throw new InvalidOperationException(
|
?? throw new InvalidOperationException(
|
||||||
$"No registration {job.RegistrationId} for RegistratieVerlopen job {job.JobId}.");
|
$"No registration {job.RegistrationId} for RegistratieVerlopen job {job.JobId}.");
|
||||||
|
|
||||||
// A redelivered job whose registration is already VERLOPEN completes without persisting again.
|
// Only a still-open registration lapses; an already-resolved one (expired, or withdrawn/decided
|
||||||
if (registration.Status == RegistrationStatus.Verlopen)
|
// while it waited) is left untouched so the job can complete without violating the aggregate.
|
||||||
|
if (registration.Status is not (RegistrationStatus.Ingediend or RegistrationStatus.InBehandeling))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
registration.Expire();
|
registration.Expire();
|
||||||
|
|||||||
Reference in New Issue
Block a user