fix: expire proposals on response boundary

This commit is contained in:
Josh Creek
2026-09-01 08:06:17 +01:00
parent eef7cf28da
commit f520584368
2 changed files with 15 additions and 1 deletions
+14
View File
@@ -161,6 +161,20 @@ func RespondToProposal(ctx context.Context, db *sql.DB, playerID, proposalID, id
if err := tx.QueryRowContext(ctx, ProposalLockSQL, proposalID).Scan(&playlist, &state, &revision, &expiresAt); err != nil {
return err
}
// A mutation is also a recovery boundary. If the response arrives after
// the window, advance both the proposal and its pending participants in
// this same transaction before returning the closed error. Otherwise a
// client that missed the expiry event could observe OPEN/PENDING forever
// when its first durable interaction is an accept/decline.
if _, err := tx.ExecContext(ctx, ProposalExpireSQL, proposalID, now); err != nil {
return err
}
if _, err := tx.ExecContext(ctx, ProposalParticipantExpireSQL, proposalID, now); err != nil {
return err
}
if !now.Before(expiresAt) {
return domain.ErrProposalClosed
}
if state != string(domain.Open) || !now.Before(expiresAt) {
return domain.ErrProposalClosed
}