fix: CompleteRaid 보상 지급 블로킹 제거
All checks were successful
Server CI/CD / lint-and-build (push) Successful in 36s
Server CI/CD / deploy (push) Successful in 51s

HTTP 핸들러에서 동기 재시도(3회 + sleep 1s/2s)를 제거하고
1회만 시도 후 실패 시 즉시 RewardFailure DB에 저장.
기존 RewardWorker가 백그라운드에서 재시도 처리.

변경 전: 최악 ~18초 블로킹 (데디서버 15초 타임아웃 초과)
변경 후: RPC 1회 × 플레이어 수 ≈ 2-3초

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 16:28:34 +09:00
parent 11d3cdfc25
commit c9af89a852

View File

@@ -221,28 +221,27 @@ func (s *Service) CompleteRaid(sessionName string, rewards []PlayerReward) (*Bos
hasRewardFailure := false
if s.rewardGrant != nil {
for _, r := range rewards {
lastTxID, grantErr := s.grantWithRetry(r.Username, r.TokenAmount, r.Assets)
// 1회만 시도 — 실패 시 즉시 RewardFailure에 저장하여 백그라운드 워커가 재시도
txID, grantErr := s.rewardGrant(r.Username, r.TokenAmount, r.Assets)
result := RewardResult{Username: r.Username, Success: grantErr == nil}
if grantErr != nil {
result.Error = grantErr.Error()
log.Printf("보상 지급 실패 (재시도 소진): %s: %v", r.Username, grantErr)
log.Printf("보상 지급 실패: %s: %v (백그라운드 재시도 예정)", r.Username, grantErr)
hasRewardFailure = true
// 실패한 보상을 DB에 기록하여 백그라운드 재시도 가능하게 함
s.saveRewardFailure(sessionName, r, grantErr, lastTxID)
s.saveRewardFailure(sessionName, r, grantErr, txID)
}
resultRewards = append(resultRewards, result)
}
}
// Grant experience to players (with retry)
// Grant experience to players (1회 시도, 실패 시 백그라운드 재시도)
if s.expGrant != nil {
for _, r := range rewards {
if r.Experience > 0 {
expErr := s.grantExpWithRetry(r.Username, r.Experience)
expErr := s.expGrant(r.Username, r.Experience)
if expErr != nil {
log.Printf("경험치 지급 실패 (재시도 소진): %s: %v", r.Username, expErr)
log.Printf("경험치 지급 실패: %s: %v (백그라운드 재시도 예정)", r.Username, expErr)
hasRewardFailure = true
// 경험치 실패도 RewardFailure에 기록 (토큰/에셋 없이 경험치만)
s.saveRewardFailure(sessionName, PlayerReward{
Username: r.Username,
Experience: r.Experience,
@@ -541,49 +540,7 @@ func (s *Service) GetServerStatus(serverName string) (*DedicatedServer, []RoomSl
return server, slots, nil
}
// --- Reward retry helpers ---
const immediateRetries = 3
// grantWithRetry attempts the reward grant up to 3 times with backoff (1s, 2s).
// Returns the last attempted transaction ID (may be empty) and the error.
func (s *Service) grantWithRetry(username string, tokenAmount uint64, assets []core.MintAssetPayload) (string, error) {
delays := []time.Duration{1 * time.Second, 2 * time.Second}
var lastErr error
var lastTxID string
for attempt := 0; attempt < immediateRetries; attempt++ {
txID, err := s.rewardGrant(username, tokenAmount, assets)
if txID != "" {
lastTxID = txID
}
if err == nil {
return txID, nil
}
lastErr = err
if attempt < len(delays) {
log.Printf("보상 지급 재시도 (%d/%d): %s: %v", attempt+1, immediateRetries, username, lastErr)
time.Sleep(delays[attempt])
}
}
return lastTxID, lastErr
}
// grantExpWithRetry attempts the experience grant up to 3 times with backoff (1s, 2s).
func (s *Service) grantExpWithRetry(username string, exp int) error {
delays := []time.Duration{1 * time.Second, 2 * time.Second}
var lastErr error
for attempt := 0; attempt < immediateRetries; attempt++ {
lastErr = s.expGrant(username, exp)
if lastErr == nil {
return nil
}
if attempt < len(delays) {
log.Printf("경험치 지급 재시도 (%d/%d): %s: %v", attempt+1, immediateRetries, username, lastErr)
time.Sleep(delays[attempt])
}
}
return lastErr
}
// --- Reward helpers ---
// saveRewardFailure records a failed reward in the DB for background retry.
func (s *Service) saveRewardFailure(sessionName string, r PlayerReward, grantErr error, lastTxID string) {