Keep candidates found at a rename's revision when clearing offers
CI / ci (push) Successful in 2m42s
CI / ci (pull_request) Successful in 2m26s

A candidate search that read a dinner after its rename committed could
store fresh offers that the rename's own clear then dropped, so a valid
candidate for the renamed dinner was refused with a 400.

clearOffers now takes the revision below which offers are stale: a rename
passes its committed revision, so it drops only offers and in-flight
searches from earlier revisions, while grounding by hand and archiving
pass math.MaxInt64 and clear everything as before. A search finishing
late at an earlier revision no longer replaces offers already found at a
later one.

Fixes MQ-103-02.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 00:05:30 -04:00
co-authored by Claude Opus 5.5
parent e07628638e
commit 8d8a5480b9
4 changed files with 88 additions and 18 deletions
+3 -3
View File
@@ -149,7 +149,7 @@ type Service struct {
// id, the candidates last offered by Candidates until they expire, so
// a candidate id is resolved here and its URL never leaves the server.
// offerSearches holds the Candidates searches running, by dinner id,
// so clearing a dinner's offers keeps them from storing theirs.
// so clearing a dinner's offers keeps them from storing stale ones.
offersMu sync.Mutex
offers map[string]*offerSet
offerSearches map[string]map[*offerSearch]struct{}
@@ -388,7 +388,7 @@ func (s *Service) Update(ctx context.Context, id string, p Patch, updatedBy stri
}
if renamed {
updateCommitted()
s.clearOffers(row.ID)
s.clearOffers(row.ID, row.GroundingRevision)
}
if regrounding {
s.stopGroundings(row.ID, row.GroundingRevision)
@@ -426,7 +426,7 @@ func (s *Service) Archive(ctx context.Context, id, updatedBy string) (Dinner, er
return Dinner{}, fmt.Errorf("dinners: archive %s: %w", id, err)
}
s.stopGroundings(row.ID, math.MaxInt64)
s.clearOffers(row.ID)
s.clearOffers(row.ID, math.MaxInt64)
return fromRow(row), nil
}