From 2da24e12fffd7761e4c3afed3f67c9a976726f59 Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Sun, 31 May 2026 12:27:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=BF=81=E7=A7=BB=E6=97=B6=E6=B8=85?= =?UTF-8?q?=E7=90=86=E6=97=A7=E4=BC=9A=E8=AF=9D=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=20Redis=20=E6=81=A2=E5=A4=8D=E5=90=8E=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E8=AE=BE=E5=A4=87=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - migrateSessions() 先 DeleteByUID 清理 Redis 同用户旧会话,再写入新会话 - 调整 recoveryLoop/healthLoop 顺序:先迁后切 healthy,消除迁移期间写竞争 - 降级期重新登录产生的会话不会与 Redis 中旧会话并存 --- internal/session/fallback_store.go | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/internal/session/fallback_store.go b/internal/session/fallback_store.go index cb24da0..391e3f2 100644 --- a/internal/session/fallback_store.go +++ b/internal/session/fallback_store.go @@ -58,9 +58,9 @@ func (fs *FallbackStore) healthLoop() { ok := fs.ping() if ok && !prev { - // Redis 恢复:先切到 Redis(新会话立即走 Redis),再迁移内存存量 - fs.healthy.Store(true) + // Redis 恢复:先迁移(内部先清旧数据)再切 healthy,避免迁移期间新请求创建 Redis 会话被误删 fs.migrateSessions() + fs.healthy.Store(true) fs.degraded.Store(false) } else if !ok && prev { // Redis 掉线,标记降级(快速恢复由 recoveryLoop 负责) @@ -74,7 +74,6 @@ func (fs *FallbackStore) healthLoop() { } // recoveryLoop 启动后持续监听退化状态,一旦降级立即启动快速恢复轮询(5s 间隔) -// 退出条件:Redis 恢复成功或 connectOnce 竞争到恢复权限后执行迁移 func (fs *FallbackStore) recoveryLoop() { ticker := time.NewTicker(recoveryInterval) defer ticker.Stop() @@ -92,9 +91,9 @@ func (fs *FallbackStore) recoveryLoop() { continue // 仍未恢复 } - // Redis 已恢复 - fs.healthy.Store(true) + // Redis 已恢复:先迁后切,避免旧会话残留 + 新请求竞争 fs.migrateSessions() + fs.healthy.Store(true) fs.degraded.Store(false) fs.recovering.Store(false) } @@ -113,7 +112,22 @@ func (fs *FallbackStore) migrateSessions() { return } + // 收集迁移涉及的用户 + userSet := make(map[uint]bool, len(sessions)) + for _, s := range sessions { + userSet[s.UserID] = true + } + ctx := context.Background() + + // 先清理 Redis 中同用户的旧会话(降级期间用户重新登录,旧会话已失效) + // 避免 Redis 恢复后出现新旧两条会话并存、设备列表重复 + for uid := range userSet { + if err := fs.redis.DeleteByUID(uid); err != nil { + log.Printf("[FallbackStore] 迁移前清理旧会话失败 uid=%d err=%v", uid, err) + } + } + pipe := fs.client.Pipeline() for _, s := range sessions { @@ -141,7 +155,7 @@ func (fs *FallbackStore) migrateSessions() { _ = fs.memory.Delete(s.ID) } - log.Printf("[FallbackStore] Redis 已恢复,静默迁移 %d 条会话(内存已清理)", len(sessions)) + log.Printf("[FallbackStore] Redis 已恢复,静默迁移 %d 条会话(旧会话已清理,内存已释放)", len(sessions)) } // ping 检查 Redis 连接状态(带超时)