fix: RequestEntry TOCTOU 경쟁 조건 수정 — 트랜잭션으로 원자화
Some checks failed
Server CI/CD / lint-and-build (push) Failing after 38s
Server CI/CD / deploy (push) Has been skipped

중복 입장 방지를 위해 active-room 체크와 room 생성을 단일 트랜잭션으로 래핑

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 18:53:33 +09:00
parent b0de89a18a
commit 9504bf37de

View File

@@ -64,17 +64,6 @@ func (s *Service) RequestEntry(usernames []string, bossID int) (*BossRoom, error
seen[u] = true
}
// Check if any player is already in an active room
for _, username := range usernames {
count, err := s.repo.CountActiveByUsername(username)
if err != nil {
return nil, fmt.Errorf("플레이어 상태 확인 실패: %w", err)
}
if count > 0 {
return nil, fmt.Errorf("플레이어 %s가 이미 보스 레이드 중입니다", username)
}
}
playersJSON, err := json.Marshal(usernames)
if err != nil {
return nil, fmt.Errorf("플레이어 목록 직렬화 실패: %w", err)
@@ -90,8 +79,24 @@ func (s *Service) RequestEntry(usernames []string, bossID int) (*BossRoom, error
Players: string(playersJSON),
}
if err := s.repo.Create(room); err != nil {
return nil, fmt.Errorf("방 생성 실패: %w", err)
// Wrap active-room check + creation in a transaction to prevent TOCTOU race.
err = s.repo.Transaction(func(txRepo *Repository) error {
for _, username := range usernames {
count, err := txRepo.CountActiveByUsername(username)
if err != nil {
return fmt.Errorf("플레이어 상태 확인 실패: %w", err)
}
if count > 0 {
return fmt.Errorf("플레이어 %s가 이미 보스 레이드 중입니다", username)
}
}
if err := txRepo.Create(room); err != nil {
return fmt.Errorf("방 생성 실패: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
return room, nil