fix: 更新会话续期逻辑,调整续期阈值和TTL设置,确保统一调度会话映射按配置正确续期

This commit is contained in:
sususu98
2025-09-10 15:53:23 +08:00
parent 1dd00e1463
commit 433f0c5f23
4 changed files with 138 additions and 24 deletions

View File

@@ -26,7 +26,7 @@ REDIS_ENABLE_TLS=
# 粘性会话TTL配置小时默认1小时 # 粘性会话TTL配置小时默认1小时
STICKY_SESSION_TTL_HOURS=1 STICKY_SESSION_TTL_HOURS=1
# 续期阈值分钟默认0分钟不续期 # 续期阈值分钟默认0分钟不续期
STICKY_SESSION_RENEWAL_THRESHOLD_MINUTES=0 STICKY_SESSION_RENEWAL_THRESHOLD_MINUTES=15
# 🎯 Claude API 配置 # 🎯 Claude API 配置
CLAUDE_API_URL=https://api.anthropic.com/v1/messages CLAUDE_API_URL=https://api.anthropic.com/v1/messages

View File

@@ -244,8 +244,8 @@ class UnifiedClaudeScheduler {
effectiveModel effectiveModel
) )
if (isAvailable) { if (isAvailable) {
// 🚀 智能会话续期剩余时间少于14天时自动续期到15天 // 🚀 智能会话续期剩余时间少于14天时自动续期到15天(续期正确的 unified 映射键)
await redis.extendSessionAccountMappingTTL(sessionHash) await this._extendSessionMappingTTL(sessionHash)
logger.info( logger.info(
`🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}` `🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
) )
@@ -806,9 +806,11 @@ class UnifiedClaudeScheduler {
async _setSessionMapping(sessionHash, accountId, accountType) { async _setSessionMapping(sessionHash, accountId, accountType) {
const client = redis.getClientSafe() const client = redis.getClientSafe()
const mappingData = JSON.stringify({ accountId, accountType }) const mappingData = JSON.stringify({ accountId, accountType })
// 依据配置设置TTL小时
// 设置1小时过期 const appConfig = require('../../config/config')
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, 3600, mappingData) const ttlHours = appConfig.session?.stickyTtlHours || 1
const ttlSeconds = Math.max(1, Math.floor(ttlHours * 60 * 60))
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, ttlSeconds, mappingData)
} }
// 🗑️ 删除会话映射 // 🗑️ 删除会话映射
@@ -817,6 +819,44 @@ class UnifiedClaudeScheduler {
await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`) await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`)
} }
// 🔁 续期统一调度会话映射TTL针对 unified_claude_session_mapping:* 键),遵循会话配置
async _extendSessionMappingTTL(sessionHash) {
try {
const client = redis.getClientSafe()
const key = `${this.SESSION_MAPPING_PREFIX}${sessionHash}`
const remainingTTL = await client.ttl(key)
// -2: key 不存在;-1: 无过期时间
if (remainingTTL === -2) return false
if (remainingTTL === -1) return true
const appConfig = require('../../config/config')
const ttlHours = appConfig.session?.stickyTtlHours || 1
const renewalThresholdMinutes = appConfig.session?.renewalThresholdMinutes || 0
// 阈值为0则不续期
if (!renewalThresholdMinutes) return true
const fullTTL = Math.max(1, Math.floor(ttlHours * 60 * 60))
const threshold = Math.max(0, Math.floor(renewalThresholdMinutes * 60))
if (remainingTTL < threshold) {
await client.expire(key, fullTTL)
logger.debug(
`🔄 Renewed unified session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 60)}m, renewed to ${ttlHours}h)`
)
} else {
logger.debug(
`✅ Unified session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 60)}m)`
)
}
return true
} catch (error) {
logger.error('❌ Failed to extend unified session TTL:', error)
return false
}
}
// 🚫 标记账户为限流状态 // 🚫 标记账户为限流状态
async markAccountRateLimited( async markAccountRateLimited(
accountId, accountId,
@@ -989,8 +1029,8 @@ class UnifiedClaudeScheduler {
requestedModel requestedModel
) )
if (isAvailable) { if (isAvailable) {
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天 // 🚀 智能会话续期:续期 unified 映射键
await redis.extendSessionAccountMappingTTL(sessionHash) await this._extendSessionMappingTTL(sessionHash)
logger.info( logger.info(
`🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}` `🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
) )
@@ -1131,8 +1171,8 @@ class UnifiedClaudeScheduler {
effectiveModel effectiveModel
) )
if (isAvailable) { if (isAvailable) {
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天 // 🚀 智能会话续期:续期 unified 映射键
await redis.extendSessionAccountMappingTTL(sessionHash) await this._extendSessionMappingTTL(sessionHash)
logger.info( logger.info(
`🎯 Using sticky CCR session account: ${mappedAccount.accountId} for session ${sessionHash}` `🎯 Using sticky CCR session account: ${mappedAccount.accountId} for session ${sessionHash}`
) )

View File

@@ -61,8 +61,8 @@ class UnifiedGeminiScheduler {
mappedAccount.accountType mappedAccount.accountType
) )
if (isAvailable) { if (isAvailable) {
// 🚀 智能会话续期剩余时间少于14天时自动续期到15天 // 🚀 智能会话续期(续期 unified 映射键,按配置)
await redis.extendSessionAccountMappingTTL(sessionHash) await this._extendSessionMappingTTL(sessionHash)
logger.info( logger.info(
`🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}` `🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
) )
@@ -285,9 +285,11 @@ class UnifiedGeminiScheduler {
async _setSessionMapping(sessionHash, accountId, accountType) { async _setSessionMapping(sessionHash, accountId, accountType) {
const client = redis.getClientSafe() const client = redis.getClientSafe()
const mappingData = JSON.stringify({ accountId, accountType }) const mappingData = JSON.stringify({ accountId, accountType })
// 依据配置设置TTL小时
// 设置1小时过期 const appConfig = require('../../config/config')
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, 3600, mappingData) const ttlHours = appConfig.session?.stickyTtlHours || 1
const ttlSeconds = Math.max(1, Math.floor(ttlHours * 60 * 60))
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, ttlSeconds, mappingData)
} }
// 🗑️ 删除会话映射 // 🗑️ 删除会话映射
@@ -296,6 +298,41 @@ class UnifiedGeminiScheduler {
await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`) await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`)
} }
// 🔁 续期统一调度会话映射TTL针对 unified_gemini_session_mapping:* 键),遵循会话配置
async _extendSessionMappingTTL(sessionHash) {
try {
const client = redis.getClientSafe()
const key = `${this.SESSION_MAPPING_PREFIX}${sessionHash}`
const remainingTTL = await client.ttl(key)
if (remainingTTL === -2) return false
if (remainingTTL === -1) return true
const appConfig = require('../../config/config')
const ttlHours = appConfig.session?.stickyTtlHours || 1
const renewalThresholdMinutes = appConfig.session?.renewalThresholdMinutes || 0
if (!renewalThresholdMinutes) return true
const fullTTL = Math.max(1, Math.floor(ttlHours * 60 * 60))
const threshold = Math.max(0, Math.floor(renewalThresholdMinutes * 60))
if (remainingTTL < threshold) {
await client.expire(key, fullTTL)
logger.debug(
`🔄 Renewed unified Gemini session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 60)}m, renewed to ${ttlHours}h)`
)
} else {
logger.debug(
`✅ Unified Gemini session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 60)}m)`
)
}
return true
} catch (error) {
logger.error('❌ Failed to extend unified Gemini session TTL:', error)
return false
}
}
// 🚫 标记账户为限流状态 // 🚫 标记账户为限流状态
async markAccountRateLimited(accountId, accountType, sessionHash = null) { async markAccountRateLimited(accountId, accountType, sessionHash = null) {
try { try {
@@ -384,8 +421,8 @@ class UnifiedGeminiScheduler {
mappedAccount.accountType mappedAccount.accountType
) )
if (isAvailable) { if (isAvailable) {
// 🚀 智能会话续期剩余时间少于14天时自动续期到15天 // 🚀 智能会话续期(续期 unified 映射键,按配置)
await redis.extendSessionAccountMappingTTL(sessionHash) await this._extendSessionMappingTTL(sessionHash)
logger.info( logger.info(
`🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}` `🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
) )

View File

@@ -90,8 +90,8 @@ class UnifiedOpenAIScheduler {
mappedAccount.accountType mappedAccount.accountType
) )
if (isAvailable) { if (isAvailable) {
// 🚀 智能会话续期剩余时间少于14天时自动续期到15天 // 🚀 智能会话续期(续期 unified 映射键,按配置)
await redis.extendSessionAccountMappingTTL(sessionHash) await this._extendSessionMappingTTL(sessionHash)
logger.info( logger.info(
`🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}` `🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
) )
@@ -291,9 +291,11 @@ class UnifiedOpenAIScheduler {
async _setSessionMapping(sessionHash, accountId, accountType) { async _setSessionMapping(sessionHash, accountId, accountType) {
const client = redis.getClientSafe() const client = redis.getClientSafe()
const mappingData = JSON.stringify({ accountId, accountType }) const mappingData = JSON.stringify({ accountId, accountType })
// 依据配置设置TTL小时
// 设置1小时过期 const appConfig = require('../../config/config')
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, 3600, mappingData) const ttlHours = appConfig.session?.stickyTtlHours || 1
const ttlSeconds = Math.max(1, Math.floor(ttlHours * 60 * 60))
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, ttlSeconds, mappingData)
} }
// 🗑️ 删除会话映射 // 🗑️ 删除会话映射
@@ -302,6 +304,41 @@ class UnifiedOpenAIScheduler {
await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`) await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`)
} }
// 🔁 续期统一调度会话映射TTL针对 unified_openai_session_mapping:* 键),遵循会话配置
async _extendSessionMappingTTL(sessionHash) {
try {
const client = redis.getClientSafe()
const key = `${this.SESSION_MAPPING_PREFIX}${sessionHash}`
const remainingTTL = await client.ttl(key)
if (remainingTTL === -2) return false
if (remainingTTL === -1) return true
const appConfig = require('../../config/config')
const ttlHours = appConfig.session?.stickyTtlHours || 1
const renewalThresholdMinutes = appConfig.session?.renewalThresholdMinutes || 0
if (!renewalThresholdMinutes) return true
const fullTTL = Math.max(1, Math.floor(ttlHours * 60 * 60))
const threshold = Math.max(0, Math.floor(renewalThresholdMinutes * 60))
if (remainingTTL < threshold) {
await client.expire(key, fullTTL)
logger.debug(
`🔄 Renewed unified OpenAI session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 60)}m, renewed to ${ttlHours}h)`
)
} else {
logger.debug(
`✅ Unified OpenAI session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 60)}m)`
)
}
return true
} catch (error) {
logger.error('❌ Failed to extend unified OpenAI session TTL:', error)
return false
}
}
// 🚫 标记账户为限流状态 // 🚫 标记账户为限流状态
async markAccountRateLimited(accountId, accountType, sessionHash = null, resetsInSeconds = null) { async markAccountRateLimited(accountId, accountType, sessionHash = null, resetsInSeconds = null) {
try { try {
@@ -408,8 +445,8 @@ class UnifiedOpenAIScheduler {
mappedAccount.accountType mappedAccount.accountType
) )
if (isAvailable) { if (isAvailable) {
// 🚀 智能会话续期剩余时间少于14天时自动续期到15天 // 🚀 智能会话续期(续期 unified 映射键,按配置)
await redis.extendSessionAccountMappingTTL(sessionHash) await this._extendSessionMappingTTL(sessionHash)
logger.info( logger.info(
`🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType})` `🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType})`
) )