Fix: entry token 만료 기반으로 stale waiting room 판단
타임아웃 대신 Redis entry token 존재 여부로 판단: - pending token이 Redis에 남아있음 → 정상 로딩 중, 방 보존 - pending token이 없음 (만료/소비) → abandoned 확정, 즉시 정리 정상 로딩 중인 파티원의 방을 보호하면서도 강제 종료 유저의 즉각적 복구 가능. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,14 +64,13 @@ func (r *Repository) CountActiveByUsername(username string) (int64, error) {
|
||||
return count, err
|
||||
}
|
||||
|
||||
// FindStaleWaitingRoomsByUsername returns waiting rooms containing the given username
|
||||
// that were created before the threshold.
|
||||
func (r *Repository) FindStaleWaitingRoomsByUsername(username string, threshold time.Time) ([]BossRoom, error) {
|
||||
// FindWaitingRoomsByUsername returns all waiting rooms containing the given username.
|
||||
func (r *Repository) FindWaitingRoomsByUsername(username string) ([]BossRoom, error) {
|
||||
escaped := strings.NewReplacer("%", "\\%", "_", "\\_").Replace(username)
|
||||
search := `"` + escaped + `"`
|
||||
var rooms []BossRoom
|
||||
err := r.db.Where("status = ? AND players LIKE ? AND created_at < ?",
|
||||
StatusWaiting, "%"+search+"%", threshold).
|
||||
err := r.db.Where("status = ? AND players LIKE ?",
|
||||
StatusWaiting, "%"+search+"%").
|
||||
Find(&rooms).Error
|
||||
return rooms, err
|
||||
}
|
||||
|
||||
@@ -414,19 +414,29 @@ func (s *Service) RequestEntryWithTokens(usernames []string, bossID int) (*BossR
|
||||
}
|
||||
|
||||
// cleanupStaleWaitingForUsers checks if any of the given users are stuck in
|
||||
// a stale waiting room (older than waitingRoomTimeout) and cleans them up.
|
||||
// This provides instant resolution for players who force-quit during loading.
|
||||
// a waiting room whose entry token has already expired or been consumed.
|
||||
// If the pending token is gone from Redis, the room is abandoned and safe to remove.
|
||||
// If the token still exists, the room may have active loading players — leave it alone.
|
||||
func (s *Service) cleanupStaleWaitingForUsers(usernames []string) {
|
||||
threshold := time.Now().Add(-waitingRoomTimeout)
|
||||
ctx := context.Background()
|
||||
for _, username := range usernames {
|
||||
rooms, err := s.repo.FindStaleWaitingRoomsByUsername(username, threshold)
|
||||
rooms, err := s.repo.FindWaitingRoomsByUsername(username)
|
||||
if err != nil || len(rooms) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// pending entry token이 Redis에 남아있으면 정상 로딩 중일 수 있음 → 보존
|
||||
pendingKey := pendingEntryPrefix + username
|
||||
exists, _ := s.rdb.Exists(ctx, pendingKey).Result()
|
||||
if exists > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 토큰 만료/소비됨 → 방 abandoned 확정, 정리
|
||||
for _, room := range rooms {
|
||||
log.Printf("stale 대기방 정리: session=%s, player=%s", room.SessionName, username)
|
||||
log.Printf("abandoned 대기방 정리 (토큰 만료): session=%s, player=%s", room.SessionName, username)
|
||||
if err := s.repo.DeleteRoomBySessionName(room.SessionName); err != nil {
|
||||
log.Printf("stale 대기방 삭제 실패: %v", err)
|
||||
log.Printf("대기방 삭제 실패: %v", err)
|
||||
}
|
||||
_ = s.repo.ResetRoomSlot(room.SessionName)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user