fix(game): clear nudges on game completion
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Has been skipped
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
Nudge badges lingered in the lobby on games that ended by turn-timeout, resignation or forfeit: those paths commit the finish directly, bypassing the move path's per-mover NudgeClearer, so the awaited seat's nudge was never marked read. A finished game's nudges are stale, so clear them all. Add a wired NudgeExpirer (social.ExpireNudges) called from the shared commit finish block — covering every completion path — and from the voidGame recovery path. It clears every seat's nudge bits for the game and leaves chat messages unread; unlike ClearNudges it records no publish-to-read latency, since a completion is an expiry, not a read. An integration test reproduces the timeout case (nudge cleared, chat kept). Docs: ARCHITECTURE §9.1, FUNCTIONAL (+_ru), backend/README.
This commit is contained in:
@@ -55,6 +55,25 @@ func (svc *Service) ClearNudges(ctx context.Context, gameID, accountID uuid.UUID
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExpireNudges marks every pending nudge in the game read, for when the game finishes: a nudge
|
||||
// badge is stale once the game is over, so completion clears them all for every seat. Chat
|
||||
// messages are left untouched (they stay unread). It satisfies game.NudgeExpirer and is wired
|
||||
// into the completion path; failures are the caller's to log (the game has already finished).
|
||||
// Unlike ClearNudges it records no publish-to-read latency — a completion is an expiry, not the
|
||||
// recipient reading the nudge — so the latency metric is not skewed by games that end hours later.
|
||||
func (svc *Service) ExpireNudges(ctx context.Context, gameID uuid.UUID) error {
|
||||
ctx, span := svc.tracer.Start(ctx, "social.ExpireNudges",
|
||||
trace.WithAttributes(attribute.String("game.id", gameID.String())))
|
||||
defer span.End()
|
||||
n, err := svc.store.expireNudges(ctx, gameID)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
return err
|
||||
}
|
||||
span.SetAttributes(attribute.Int64("expired.count", n))
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnreadGames returns the set of games in which viewerID has at least one unread chat
|
||||
// entry, for seeding the lobby's per-card unread badge in a single query.
|
||||
func (svc *Service) UnreadGames(ctx context.Context, viewerID uuid.UUID) (map[uuid.UUID]bool, error) {
|
||||
@@ -140,6 +159,21 @@ RETURNING m.created_at`
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// expireNudges marks every still-unread nudge in the game read for all seats at once, used when
|
||||
// the game finishes. It returns the number of nudge entries it cleared. Only 'nudge' rows are
|
||||
// touched, so chat messages keep their unread bits; it returns no post times because a completion
|
||||
// is an expiry, not a player reading, and must not feed the publish-to-read latency metric.
|
||||
func (s *Store) expireNudges(ctx context.Context, gameID uuid.UUID) (int64, error) {
|
||||
const q = `UPDATE backend.chat_messages
|
||||
SET unread_seats = 0
|
||||
WHERE game_id = $1 AND kind = 'nudge' AND unread_seats <> 0`
|
||||
res, err := s.db.ExecContext(ctx, q, gameID)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("social: expire nudges: %w", err)
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
// unreadGames returns the games where viewerID has an unread entry, resolving their
|
||||
// seat per game through the game_players join.
|
||||
func (s *Store) unreadGames(ctx context.Context, viewerID uuid.UUID) (map[uuid.UUID]bool, error) {
|
||||
|
||||
Reference in New Issue
Block a user