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
|
return count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindStaleWaitingRoomsByUsername returns waiting rooms containing the given username
|
// FindWaitingRoomsByUsername returns all waiting rooms containing the given username.
|
||||||
// that were created before the threshold.
|
func (r *Repository) FindWaitingRoomsByUsername(username string) ([]BossRoom, error) {
|
||||||
func (r *Repository) FindStaleWaitingRoomsByUsername(username string, threshold time.Time) ([]BossRoom, error) {
|
|
||||||
escaped := strings.NewReplacer("%", "\\%", "_", "\\_").Replace(username)
|
escaped := strings.NewReplacer("%", "\\%", "_", "\\_").Replace(username)
|
||||||
search := `"` + escaped + `"`
|
search := `"` + escaped + `"`
|
||||||
var rooms []BossRoom
|
var rooms []BossRoom
|
||||||
err := r.db.Where("status = ? AND players LIKE ? AND created_at < ?",
|
err := r.db.Where("status = ? AND players LIKE ?",
|
||||||
StatusWaiting, "%"+search+"%", threshold).
|
StatusWaiting, "%"+search+"%").
|
||||||
Find(&rooms).Error
|
Find(&rooms).Error
|
||||||
return rooms, err
|
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
|
// cleanupStaleWaitingForUsers checks if any of the given users are stuck in
|
||||||
// a stale waiting room (older than waitingRoomTimeout) and cleans them up.
|
// a waiting room whose entry token has already expired or been consumed.
|
||||||
// This provides instant resolution for players who force-quit during loading.
|
// 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) {
|
func (s *Service) cleanupStaleWaitingForUsers(usernames []string) {
|
||||||
threshold := time.Now().Add(-waitingRoomTimeout)
|
ctx := context.Background()
|
||||||
for _, username := range usernames {
|
for _, username := range usernames {
|
||||||
rooms, err := s.repo.FindStaleWaitingRoomsByUsername(username, threshold)
|
rooms, err := s.repo.FindWaitingRoomsByUsername(username)
|
||||||
if err != nil || len(rooms) == 0 {
|
if err != nil || len(rooms) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pending entry token이 Redis에 남아있으면 정상 로딩 중일 수 있음 → 보존
|
||||||
|
pendingKey := pendingEntryPrefix + username
|
||||||
|
exists, _ := s.rdb.Exists(ctx, pendingKey).Result()
|
||||||
|
if exists > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 토큰 만료/소비됨 → 방 abandoned 확정, 정리
|
||||||
for _, room := range rooms {
|
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 {
|
if err := s.repo.DeleteRoomBySessionName(room.SessionName); err != nil {
|
||||||
log.Printf("stale 대기방 삭제 실패: %v", err)
|
log.Printf("대기방 삭제 실패: %v", err)
|
||||||
}
|
}
|
||||||
_ = s.repo.ResetRoomSlot(room.SessionName)
|
_ = s.repo.ResetRoomSlot(room.SessionName)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user