feat: 优化粘性会话TTL管理策略

- 将默认TTL从1小时延长至15天,更适合长期项目开发
- 实现智能续期机制:剩余时间<14天时自动续期到15天
- 添加配置化支持:通过环境变量STICKY_SESSION_TTL_DAYS和STICKY_SESSION_RENEWAL_THRESHOLD_DAYS调整TTL策略
- 集成到所有调度器:Claude、OpenAI、Gemini的普通会话和分组会话
- 提升用户体验:活跃项目会话持续有效,停用项目自动清理
- 性能优化:智能判断减少不必要的Redis EXPIRE操作

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Edric Li
2025-09-08 00:43:33 +08:00
parent 9fa7602947
commit 9cbf3195e0
6 changed files with 74 additions and 2 deletions

View File

@@ -1356,9 +1356,11 @@ class RedisClient {
}
// 🔗 会话sticky映射管理
async setSessionAccountMapping(sessionHash, accountId, ttl = 3600) {
async setSessionAccountMapping(sessionHash, accountId, ttl = null) {
const appConfig = require('../../config/config')
const defaultTTL = ttl !== null ? ttl : (appConfig.session?.stickyTtlDays || 15) * 24 * 60 * 60
const key = `sticky_session:${sessionHash}`
await this.client.set(key, accountId, 'EX', ttl)
await this.client.set(key, accountId, 'EX', defaultTTL)
}
async getSessionAccountMapping(sessionHash) {
@@ -1366,6 +1368,52 @@ class RedisClient {
return await this.client.get(key)
}
// 🚀 智能会话TTL续期剩余时间少于阈值时自动续期
async extendSessionAccountMappingTTL(sessionHash) {
const appConfig = require('../../config/config')
const key = `sticky_session:${sessionHash}`
// 📊 从配置获取参数
const ttlDays = appConfig.session?.stickyTtlDays || 15
const thresholdDays = appConfig.session?.renewalThresholdDays || 14
const fullTTL = ttlDays * 24 * 60 * 60 // 转换为秒
const renewalThreshold = thresholdDays * 24 * 60 * 60 // 转换为秒
try {
// 获取当前剩余TTL
const remainingTTL = await this.client.ttl(key)
// 键不存在或已过期
if (remainingTTL === -2) {
return false
}
// 键存在但没有TTL永不过期不需要处理
if (remainingTTL === -1) {
return true
}
// 🎯 智能续期策略:仅在剩余时间少于阈值时才续期
if (remainingTTL < renewalThreshold) {
await this.client.expire(key, fullTTL)
logger.debug(
`🔄 Renewed sticky session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 24 / 3600)}d, renewed to ${ttlDays}d)`
)
return true
}
// 剩余时间充足,无需续期
logger.debug(
`✅ Sticky session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 24 / 3600)}d)`
)
return true
} catch (error) {
logger.error('❌ Failed to extend session TTL:', error)
return false
}
}
async deleteSessionAccountMapping(sessionHash) {
const key = `sticky_session:${sessionHash}`
return await this.client.del(key)