fix: CompleteRaid 보상 지급 블로킹 제거
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:
@@ -221,28 +221,27 @@ func (s *Service) CompleteRaid(sessionName string, rewards []PlayerReward) (*Bos
|
|||||||
hasRewardFailure := false
|
hasRewardFailure := false
|
||||||
if s.rewardGrant != nil {
|
if s.rewardGrant != nil {
|
||||||
for _, r := range rewards {
|
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}
|
result := RewardResult{Username: r.Username, Success: grantErr == nil}
|
||||||
if grantErr != nil {
|
if grantErr != nil {
|
||||||
result.Error = grantErr.Error()
|
result.Error = grantErr.Error()
|
||||||
log.Printf("보상 지급 실패 (재시도 소진): %s: %v", r.Username, grantErr)
|
log.Printf("보상 지급 실패: %s: %v (백그라운드 재시도 예정)", r.Username, grantErr)
|
||||||
hasRewardFailure = true
|
hasRewardFailure = true
|
||||||
// 실패한 보상을 DB에 기록하여 백그라운드 재시도 가능하게 함
|
s.saveRewardFailure(sessionName, r, grantErr, txID)
|
||||||
s.saveRewardFailure(sessionName, r, grantErr, lastTxID)
|
|
||||||
}
|
}
|
||||||
resultRewards = append(resultRewards, result)
|
resultRewards = append(resultRewards, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grant experience to players (with retry)
|
// Grant experience to players (1회 시도, 실패 시 백그라운드 재시도)
|
||||||
if s.expGrant != nil {
|
if s.expGrant != nil {
|
||||||
for _, r := range rewards {
|
for _, r := range rewards {
|
||||||
if r.Experience > 0 {
|
if r.Experience > 0 {
|
||||||
expErr := s.grantExpWithRetry(r.Username, r.Experience)
|
expErr := s.expGrant(r.Username, r.Experience)
|
||||||
if expErr != nil {
|
if expErr != nil {
|
||||||
log.Printf("경험치 지급 실패 (재시도 소진): %s: %v", r.Username, expErr)
|
log.Printf("경험치 지급 실패: %s: %v (백그라운드 재시도 예정)", r.Username, expErr)
|
||||||
hasRewardFailure = true
|
hasRewardFailure = true
|
||||||
// 경험치 실패도 RewardFailure에 기록 (토큰/에셋 없이 경험치만)
|
|
||||||
s.saveRewardFailure(sessionName, PlayerReward{
|
s.saveRewardFailure(sessionName, PlayerReward{
|
||||||
Username: r.Username,
|
Username: r.Username,
|
||||||
Experience: r.Experience,
|
Experience: r.Experience,
|
||||||
@@ -541,49 +540,7 @@ func (s *Service) GetServerStatus(serverName string) (*DedicatedServer, []RoomSl
|
|||||||
return server, slots, nil
|
return server, slots, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Reward retry helpers ---
|
// --- Reward 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// saveRewardFailure records a failed reward in the DB for background retry.
|
// saveRewardFailure records a failed reward in the DB for background retry.
|
||||||
func (s *Service) saveRewardFailure(sessionName string, r PlayerReward, grantErr error, lastTxID string) {
|
func (s *Service) saveRewardFailure(sessionName string, r PlayerReward, grantErr error, lastTxID string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user