mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-03-30 00:33:35 +00:00
Merge pull request #1008 from DragonFSKY/fix/sticky-session-temp-unavailable-fallback [skip ci]
fix: 补全账户临时不可用检查,修复粘性会话不自动切换
This commit is contained in:
@@ -478,33 +478,40 @@ class UnifiedClaudeScheduler {
|
||||
boundAccount.status !== 'blocked' &&
|
||||
boundAccount.status !== 'temp_error'
|
||||
) {
|
||||
const isRateLimited = await claudeAccountService.isAccountRateLimited(boundAccount.id)
|
||||
if (isRateLimited) {
|
||||
const rateInfo = await claudeAccountService.getAccountRateLimitInfo(boundAccount.id)
|
||||
const error = new Error('Dedicated Claude account is rate limited')
|
||||
error.code = 'CLAUDE_DEDICATED_RATE_LIMITED'
|
||||
error.accountId = boundAccount.id
|
||||
error.rateLimitEndAt = rateInfo?.rateLimitEndAt || boundAccount.rateLimitEndAt || null
|
||||
throw error
|
||||
}
|
||||
|
||||
if (!isSchedulable(boundAccount.schedulable)) {
|
||||
// 检查是否临时不可用
|
||||
if (await this.isAccountTemporarilyUnavailable(boundAccount.id, 'claude-official')) {
|
||||
logger.warn(
|
||||
`⚠️ Bound Claude OAuth account ${apiKeyData.claudeAccountId} is not schedulable (schedulable: ${boundAccount?.schedulable})`
|
||||
`⏱️ Bound Claude OAuth account ${apiKeyData.claudeAccountId} is temporarily unavailable in pool selection, falling back to shared pool`
|
||||
)
|
||||
} else {
|
||||
logger.info(
|
||||
`🎯 Using bound dedicated Claude OAuth account: ${boundAccount.name} (${apiKeyData.claudeAccountId})`
|
||||
)
|
||||
return [
|
||||
{
|
||||
...boundAccount,
|
||||
accountId: boundAccount.id,
|
||||
accountType: 'claude-official',
|
||||
priority: parseInt(boundAccount.priority) || 50,
|
||||
lastUsedAt: boundAccount.lastUsedAt || '0'
|
||||
}
|
||||
]
|
||||
const isRateLimited = await claudeAccountService.isAccountRateLimited(boundAccount.id)
|
||||
if (isRateLimited) {
|
||||
const rateInfo = await claudeAccountService.getAccountRateLimitInfo(boundAccount.id)
|
||||
const error = new Error('Dedicated Claude account is rate limited')
|
||||
error.code = 'CLAUDE_DEDICATED_RATE_LIMITED'
|
||||
error.accountId = boundAccount.id
|
||||
error.rateLimitEndAt = rateInfo?.rateLimitEndAt || boundAccount.rateLimitEndAt || null
|
||||
throw error
|
||||
}
|
||||
|
||||
if (!isSchedulable(boundAccount.schedulable)) {
|
||||
logger.warn(
|
||||
`⚠️ Bound Claude OAuth account ${apiKeyData.claudeAccountId} is not schedulable (schedulable: ${boundAccount?.schedulable})`
|
||||
)
|
||||
} else {
|
||||
logger.info(
|
||||
`🎯 Using bound dedicated Claude OAuth account: ${boundAccount.name} (${apiKeyData.claudeAccountId})`
|
||||
)
|
||||
return [
|
||||
{
|
||||
...boundAccount,
|
||||
accountId: boundAccount.id,
|
||||
accountType: 'claude-official',
|
||||
priority: parseInt(boundAccount.priority) || 50,
|
||||
lastUsedAt: boundAccount.lastUsedAt || '0'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn(
|
||||
@@ -534,6 +541,12 @@ class UnifiedClaudeScheduler {
|
||||
// 继续使用该账号
|
||||
}
|
||||
|
||||
// 检查是否临时不可用
|
||||
const isTempUnavailable = await this.isAccountTemporarilyUnavailable(
|
||||
boundConsoleAccount.id,
|
||||
'claude-console'
|
||||
)
|
||||
|
||||
// 检查限流状态和额度状态
|
||||
const isRateLimited = await claudeConsoleAccountService.isAccountRateLimited(
|
||||
boundConsoleAccount.id
|
||||
@@ -542,7 +555,7 @@ class UnifiedClaudeScheduler {
|
||||
boundConsoleAccount.id
|
||||
)
|
||||
|
||||
if (!isRateLimited && !isQuotaExceeded) {
|
||||
if (!isTempUnavailable && !isRateLimited && !isQuotaExceeded) {
|
||||
logger.info(
|
||||
`🎯 Using bound dedicated Claude Console account: ${boundConsoleAccount.name} (${apiKeyData.claudeConsoleAccountId})`
|
||||
)
|
||||
@@ -573,18 +586,25 @@ class UnifiedClaudeScheduler {
|
||||
boundBedrockAccountResult.data.isActive === true &&
|
||||
isSchedulable(boundBedrockAccountResult.data.schedulable)
|
||||
) {
|
||||
logger.info(
|
||||
`🎯 Using bound dedicated Bedrock account: ${boundBedrockAccountResult.data.name} (${apiKeyData.bedrockAccountId})`
|
||||
)
|
||||
return [
|
||||
{
|
||||
...boundBedrockAccountResult.data,
|
||||
accountId: boundBedrockAccountResult.data.id,
|
||||
accountType: 'bedrock',
|
||||
priority: parseInt(boundBedrockAccountResult.data.priority) || 50,
|
||||
lastUsedAt: boundBedrockAccountResult.data.lastUsedAt || '0'
|
||||
}
|
||||
]
|
||||
// 检查是否临时不可用
|
||||
if (await this.isAccountTemporarilyUnavailable(apiKeyData.bedrockAccountId, 'bedrock')) {
|
||||
logger.warn(
|
||||
`⏱️ Bound Bedrock account ${apiKeyData.bedrockAccountId} is temporarily unavailable, falling back to shared pool`
|
||||
)
|
||||
} else {
|
||||
logger.info(
|
||||
`🎯 Using bound dedicated Bedrock account: ${boundBedrockAccountResult.data.name} (${apiKeyData.bedrockAccountId})`
|
||||
)
|
||||
return [
|
||||
{
|
||||
...boundBedrockAccountResult.data,
|
||||
accountId: boundBedrockAccountResult.data.id,
|
||||
accountType: 'bedrock',
|
||||
priority: parseInt(boundBedrockAccountResult.data.priority) || 50,
|
||||
lastUsedAt: boundBedrockAccountResult.data.lastUsedAt || '0'
|
||||
}
|
||||
]
|
||||
}
|
||||
} else {
|
||||
logger.warn(
|
||||
`⚠️ Bound Bedrock account ${apiKeyData.bedrockAccountId} is not available (isActive: ${boundBedrockAccountResult?.data?.isActive}, schedulable: ${boundBedrockAccountResult?.data?.schedulable})`
|
||||
@@ -989,6 +1009,11 @@ class UnifiedClaudeScheduler {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否临时不可用
|
||||
if (await this.isAccountTemporarilyUnavailable(accountId, 'claude-official')) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否限流或过载
|
||||
const isRateLimited = await claudeAccountService.isAccountRateLimited(accountId)
|
||||
const isOverloaded = await claudeAccountService.isAccountOverloaded(accountId)
|
||||
@@ -1053,6 +1078,11 @@ class UnifiedClaudeScheduler {
|
||||
// 继续处理
|
||||
}
|
||||
|
||||
// 检查是否临时不可用
|
||||
if (await this.isAccountTemporarilyUnavailable(accountId, 'claude-console')) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否被限流
|
||||
if (await claudeConsoleAccountService.isAccountRateLimited(accountId)) {
|
||||
return false
|
||||
@@ -1091,6 +1121,11 @@ class UnifiedClaudeScheduler {
|
||||
logger.info(`🚫 Bedrock account ${accountId} is not schedulable`)
|
||||
return false
|
||||
}
|
||||
// 检查是否临时不可用
|
||||
if (await this.isAccountTemporarilyUnavailable(accountId, 'bedrock')) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Bedrock账户暂不需要限流检查,因为AWS管理限流
|
||||
return true
|
||||
} else if (accountType === 'ccr') {
|
||||
@@ -1130,6 +1165,11 @@ class UnifiedClaudeScheduler {
|
||||
// 继续处理
|
||||
}
|
||||
|
||||
// 检查是否临时不可用
|
||||
if (await this.isAccountTemporarilyUnavailable(accountId, 'ccr')) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否被限流
|
||||
if (await ccrAccountService.isAccountRateLimited(accountId)) {
|
||||
return false
|
||||
@@ -1532,6 +1572,11 @@ class UnifiedClaudeScheduler {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查是否临时不可用
|
||||
if (await this.isAccountTemporarilyUnavailable(account.id, accountType)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查是否被限流
|
||||
const isRateLimited = await this.isAccountRateLimited(account.id, accountType)
|
||||
if (isRateLimited) {
|
||||
@@ -1708,6 +1753,11 @@ class UnifiedClaudeScheduler {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查是否临时不可用
|
||||
if (await this.isAccountTemporarilyUnavailable(account.id, 'ccr')) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查是否被限流或超额
|
||||
const isRateLimited = await ccrAccountService.isAccountRateLimited(account.id)
|
||||
const isQuotaExceeded = await ccrAccountService.isAccountQuotaExceeded(account.id)
|
||||
|
||||
Reference in New Issue
Block a user