feat(auth): Add retry logic with exponential backoff for S2A and Codex API
- Add exponential backoff with jitter to S2A OAuth submission (3 attempts) - Implement 5xx error retry mechanism in Codex API workspace selection (5 attempts) - Add 429 rate limit handling with retry support in Codex API - Improve team member processing with staggered delays to avoid concurrent conflicts - Add per-attempt proxy rotation to avoid reusing failed proxies - Enhance retry delay calculation with random jitter to prevent thundering herd - Update logging to display retry attempts and delay durations - Improve error messages with HTTP status codes and response body snippets - Refactor retry loops to use consistent exponential backoff pattern across modules
This commit is contained in:
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -775,24 +776,31 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
}
|
||||
defer func() { <-s2aSem }()
|
||||
|
||||
// 从代理池获取随机代理(默认轮询使用代理池,无代理则直连)
|
||||
proxyToUse, _ := database.Instance.GetRandomCodexProxy()
|
||||
proxyDisplay := "直连"
|
||||
if proxyToUse != "" {
|
||||
proxyDisplay = getProxyDisplay(proxyToUse)
|
||||
}
|
||||
logger.Status(fmt.Sprintf("%s 入库中... | 邮箱: %s | 代理: %s", memberLogPrefix, memberEmail, proxyDisplay), memberEmail, "team")
|
||||
|
||||
var s2aSuccess bool
|
||||
var lastError string
|
||||
|
||||
for attempt := 0; attempt < 2; attempt++ { // 最多重试1次
|
||||
for attempt := 0; attempt < 3; attempt++ { // 最多重试2次
|
||||
// 检查停止信号
|
||||
if isStopped() {
|
||||
return false
|
||||
}
|
||||
if attempt > 0 {
|
||||
logger.Warning(fmt.Sprintf("%s 入库重试 (第%d次)", memberLogPrefix, attempt+1), memberEmail, "team")
|
||||
// 重试前等待一段时间,避免密集请求
|
||||
retryDelay := time.Duration(3+attempt*2) * time.Second
|
||||
logger.Warning(fmt.Sprintf("%s 入库重试 (第%d次, 等待 %ds)", memberLogPrefix, attempt+1, int(retryDelay.Seconds())), memberEmail, "team")
|
||||
time.Sleep(retryDelay)
|
||||
}
|
||||
|
||||
// 每次尝试都从代理池获取新代理(避免复用失败代理)
|
||||
proxyToUse, _ := database.Instance.GetRandomCodexProxy()
|
||||
proxyDisplay := "直连"
|
||||
if proxyToUse != "" {
|
||||
proxyDisplay = getProxyDisplay(proxyToUse)
|
||||
}
|
||||
if attempt == 0 {
|
||||
logger.Status(fmt.Sprintf("%s 入库中... | 邮箱: %s | 代理: %s", memberLogPrefix, memberEmail, proxyDisplay), memberEmail, "team")
|
||||
} else {
|
||||
logger.Status(fmt.Sprintf("%s 入库重试中... | 代理: %s", memberLogPrefix, proxyDisplay), memberEmail, "team")
|
||||
}
|
||||
|
||||
// 创建日志回调 - 只显示关键步骤
|
||||
@@ -948,13 +956,14 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul
|
||||
memberMu.Unlock()
|
||||
logger.Success(fmt.Sprintf("%s ✓ 注册成功 (耗时: %.1fs)", memberLogPrefix, regDuration.Seconds()), currentEmail, "team")
|
||||
|
||||
// 流水线:注册成功后等待 3 秒再开始入库(避免触发邮箱验证)
|
||||
// 流水线:注册成功后等待再开始入库(避免触发邮箱验证 + 成员间错开避免并发冲突)
|
||||
s2aWg.Add(1)
|
||||
go func(idx int, e, p string) {
|
||||
defer s2aWg.Done()
|
||||
// 等待 3 秒,让账号状态稳定(可被停止信号中断)
|
||||
// 基础 3 秒 + 成员索引 * 2 秒错开 + 0~2 秒随机抖动,避免同 Team 多成员同时选工作区
|
||||
stagger := 3*time.Second + time.Duration(idx*2)*time.Second + time.Duration(rand.Intn(2000))*time.Millisecond
|
||||
select {
|
||||
case <-time.After(3 * time.Second):
|
||||
case <-time.After(stagger):
|
||||
case <-teamProcessState.stopCh:
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user