fix: sync scoped connection and outbox exclusion updates
This commit is contained in:
parent
aa21bd04e1
commit
cbccd6a8d9
58 changed files with 919 additions and 1435 deletions
|
|
@ -19,9 +19,6 @@ import (
|
|||
// ErrSessionNotFound 表示目标 session 当前无活跃连接。
|
||||
var ErrSessionNotFound = errors.New("session not found")
|
||||
|
||||
// ErrSessionAmbiguous 表示仅用 session_id 无法唯一定位连接。
|
||||
var ErrSessionAmbiguous = errors.New("session id is shared by multiple auth keys")
|
||||
|
||||
var (
|
||||
ErrSessionActivationSuperseded = errors.New("session activation superseded")
|
||||
ErrSessionActivationFence = errors.New("session activation could not fence previous writer")
|
||||
|
|
@ -119,8 +116,8 @@ type SessionLifecycleObserver interface {
|
|||
|
||||
// SessionManager 是活跃连接注册表,支持按 session / auth-key / user 查找并主动 push。
|
||||
//
|
||||
// 它管理运行态的在线连接,与持久化的 store.SessionStore 互补:后者记录 session 数据,
|
||||
// 前者持有可发送的活跃连接。所有方法并发安全。
|
||||
// 它只管理进程内运行态,持有可发送的活跃连接;协议可恢复事实由 auth key、客户端重连
|
||||
// 和 durable updates/difference 链路承担。所有方法并发安全。
|
||||
type SessionManager struct {
|
||||
mu sync.RWMutex
|
||||
bySession map[sessionKey]*Conn
|
||||
|
|
@ -129,7 +126,6 @@ type SessionManager struct {
|
|||
// is on the wire and PublishActivation validates the same owner.
|
||||
claims map[sessionKey]*Conn
|
||||
claimsByAuth map[[8]byte]map[int64]*Conn // raw authKeyID -> sessionID -> provisional claim
|
||||
bySessionID map[int64]map[[8]byte]*Conn // sessionID → raw authKeyID → Conn,用于兼容旧 API 的唯一性检查
|
||||
byAuthKey map[[8]byte]map[int64]*Conn // raw authKeyID → sessionID → Conn
|
||||
byBusinessAuthKey map[[8]byte]map[sessionKey]*Conn
|
||||
byUser map[int64]map[sessionKey]*Conn
|
||||
|
|
@ -154,7 +150,6 @@ func NewSessionManager(log *zap.Logger) *SessionManager {
|
|||
bySession: make(map[sessionKey]*Conn),
|
||||
claims: make(map[sessionKey]*Conn),
|
||||
claimsByAuth: make(map[[8]byte]map[int64]*Conn),
|
||||
bySessionID: make(map[int64]map[[8]byte]*Conn),
|
||||
byAuthKey: make(map[[8]byte]map[int64]*Conn),
|
||||
byBusinessAuthKey: make(map[[8]byte]map[sessionKey]*Conn),
|
||||
byUser: make(map[int64]map[sessionKey]*Conn),
|
||||
|
|
@ -189,7 +184,7 @@ func (m *SessionManager) BeginActivation(c *Conn) error {
|
|||
key := connSessionKey(c)
|
||||
retired := make([]*Conn, 0, 2)
|
||||
m.mu.Lock()
|
||||
if c.terminal.Load() || !c.isPhysicalTransportCurrentOpen() || c.lifecycleState() != connLifecycleClaiming {
|
||||
if !c.isPhysicalTransportCurrentOpen() || c.lifecycleState() != connLifecycleClaiming {
|
||||
c.beginTerminalShutdown()
|
||||
m.mu.Unlock()
|
||||
return ErrConnClosed
|
||||
|
|
@ -248,7 +243,7 @@ func (m *SessionManager) PublishActivation(c *Conn) error {
|
|||
if m.claims[key] != c {
|
||||
return ErrSessionActivationSuperseded
|
||||
}
|
||||
if c.terminal.Load() || c.lifecycleState() != connLifecycleClaiming {
|
||||
if c.lifecycleState() != connLifecycleClaiming {
|
||||
m.removeClaimLocked(key, c)
|
||||
return ErrConnClosed
|
||||
}
|
||||
|
|
@ -265,7 +260,6 @@ func (m *SessionManager) PublishActivation(c *Conn) error {
|
|||
}
|
||||
m.removeClaimLocked(key, c)
|
||||
m.bySession[key] = c
|
||||
addSessionIDIndex(m.bySessionID, c.sessionID, c.authKeyID, c)
|
||||
addConnIndex(m.byAuthKey, c.authKeyID, c.sessionID, c)
|
||||
if businessAuthKeyID, resolved := c.BusinessAuthKeyID(); resolved {
|
||||
addBusinessAuthKeyIndex(m.byBusinessAuthKey, businessAuthKeyID, key, c)
|
||||
|
|
@ -306,32 +300,6 @@ func (m *SessionManager) AbortActivation(c *Conn) {
|
|||
}
|
||||
}
|
||||
|
||||
// Register is retained for tests and embedders that do not have a wire-level
|
||||
// required-control barrier. Production encrypted traffic uses the explicit
|
||||
// BeginActivation -> SendRequiredControl -> PublishActivation sequence.
|
||||
func (m *SessionManager) Register(c *Conn) error {
|
||||
if c == nil {
|
||||
return ErrSessionActivationSuperseded
|
||||
}
|
||||
if c.isActive() {
|
||||
m.mu.RLock()
|
||||
current := m.bySession[connSessionKey(c)]
|
||||
m.mu.RUnlock()
|
||||
if current == c {
|
||||
return nil
|
||||
}
|
||||
return ErrSessionActivationSuperseded
|
||||
}
|
||||
if err := m.BeginActivation(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.PublishActivation(c); err != nil {
|
||||
m.AbortActivation(c)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unregister 注销一个连接(仅当它仍是当前注册的同一对象,避免误删重连后的新连接)。
|
||||
// 观察者对未登录连接(userID=0)也回调:业务层据此清理按 session 维度的缓存条目,
|
||||
// 否则未登录连接的元数据只能等容量上限驱逐。
|
||||
|
|
@ -373,38 +341,6 @@ func (m *SessionManager) Unregister(c *Conn) {
|
|||
}
|
||||
}
|
||||
|
||||
// DestroySession 移除指定 session 的运行态索引,供 MTProto destroy_session 使用。
|
||||
func (m *SessionManager) DestroySession(sessionID int64) bool {
|
||||
m.mu.Lock()
|
||||
c, key, ok, ambiguous := m.uniqueSessionLocked(sessionID)
|
||||
if ambiguous || !ok {
|
||||
if !ambiguous {
|
||||
m.dropPendingBySessionLocked(sessionID)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
offlineUser := m.retireConnLocked(c, true)
|
||||
lastForUser := offlineUser != 0 && len(m.byUser[offlineUser]) == 0
|
||||
observer := m.lifecycle
|
||||
m.log.Debug("Session destroyed",
|
||||
zap.String("auth_key_id", sessionKeyLog(key.authKeyID)),
|
||||
zap.Int64("session_id", sessionID),
|
||||
zap.Int("online", len(m.bySession)),
|
||||
)
|
||||
m.mu.Unlock()
|
||||
if !forceCloseConnBatch([]*Conn{c}, forceCloseBatchTimeout) {
|
||||
m.log.Warn("Destroyed session close exceeded shared deadline",
|
||||
zap.String("auth_key_id", sessionKeyLog(key.authKeyID)),
|
||||
zap.Int64("session_id", sessionID),
|
||||
)
|
||||
}
|
||||
if observer != nil && offlineUser != 0 {
|
||||
observer.SessionOffline(key.authKeyID, sessionID, offlineUser, lastForUser)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DestroySessionForAuthKey 精确移除某个 raw auth_key_id 下的 session。
|
||||
func (m *SessionManager) DestroySessionForAuthKey(authKeyID [8]byte, sessionID int64) bool {
|
||||
m.mu.Lock()
|
||||
|
|
@ -447,22 +383,6 @@ func (m *SessionManager) DestroySessionForAuthKey(authKeyID [8]byte, sessionID i
|
|||
return true
|
||||
}
|
||||
|
||||
// BindUser 缓存 session 的授权用户。userID=0 表示当前 auth_key 已确认未登录。
|
||||
// 登录后绑定非 0 userID,使其可经 PushToUser 收到推送。
|
||||
func (m *SessionManager) BindUser(sessionID, userID int64) {
|
||||
m.mu.Lock()
|
||||
c, key, ok, ambiguous := m.uniqueSessionLocked(sessionID)
|
||||
if ambiguous || !ok {
|
||||
if ambiguous {
|
||||
m.log.Warn("Skip BindUser for ambiguous session_id", zap.Int64("session_id", sessionID))
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
m.bindUserLocked(c, key, userID)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// BindUserForAuthKey 缓存指定 raw auth_key_id + session_id 的授权用户。
|
||||
func (m *SessionManager) BindUserForAuthKey(authKeyID [8]byte, sessionID, userID int64) {
|
||||
m.mu.Lock()
|
||||
|
|
@ -500,48 +420,6 @@ func (m *SessionManager) bindUserLocked(c *Conn, key sessionKey, userID int64) {
|
|||
}
|
||||
}
|
||||
|
||||
// UserID 返回 session 当前缓存的登录用户 id。未绑定或离线时 ok=false。
|
||||
func (m *SessionManager) UserID(sessionID int64) (int64, bool) {
|
||||
m.mu.RLock()
|
||||
c, _, ok, ambiguous := m.uniqueSessionLocked(sessionID)
|
||||
m.mu.RUnlock()
|
||||
if ambiguous || !ok {
|
||||
return 0, false
|
||||
}
|
||||
userID := c.userID.Load()
|
||||
if userID == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return userID, true
|
||||
}
|
||||
|
||||
// UserIDForAuthKey 返回指定 raw auth_key_id + session_id 当前缓存的登录用户 id。
|
||||
func (m *SessionManager) UserIDForAuthKey(authKeyID [8]byte, sessionID int64) (int64, bool) {
|
||||
m.mu.RLock()
|
||||
c, ok := m.bySession[sessionKey{authKeyID: authKeyID, sessionID: sessionID}]
|
||||
m.mu.RUnlock()
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
userID := c.userID.Load()
|
||||
if userID == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return userID, true
|
||||
}
|
||||
|
||||
// UserIDResolved 返回 session 的 user_id 授权状态是否已经查过。
|
||||
// resolved=true 且 userID=0 表示该 session 当前未登录。
|
||||
func (m *SessionManager) UserIDResolved(sessionID int64) (int64, bool) {
|
||||
m.mu.RLock()
|
||||
c, _, ok, ambiguous := m.uniqueSessionLocked(sessionID)
|
||||
m.mu.RUnlock()
|
||||
if ambiguous || !ok {
|
||||
return 0, false
|
||||
}
|
||||
return c.UserIDResolved()
|
||||
}
|
||||
|
||||
// UserIDResolvedForAuthKey 返回指定 raw auth_key_id + session_id 的 user_id 缓存状态。
|
||||
func (m *SessionManager) UserIDResolvedForAuthKey(authKeyID [8]byte, sessionID int64) (int64, bool) {
|
||||
m.mu.RLock()
|
||||
|
|
@ -553,21 +431,6 @@ func (m *SessionManager) UserIDResolvedForAuthKey(authKeyID [8]byte, sessionID i
|
|||
return c.UserIDResolved()
|
||||
}
|
||||
|
||||
// BindAuthKey 缓存业务视角 auth_key_id(temp auth_key 解析后的 perm auth_key)。
|
||||
func (m *SessionManager) BindAuthKey(sessionID int64, authKeyID [8]byte) {
|
||||
m.mu.Lock()
|
||||
c, key, ok, ambiguous := m.uniqueSessionLocked(sessionID)
|
||||
if ambiguous || !ok {
|
||||
if ambiguous {
|
||||
m.log.Warn("Skip BindAuthKey for ambiguous session_id", zap.Int64("session_id", sessionID))
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
m.bindAuthKeyLocked(c, key, authKeyID)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// BindAuthKeyForSession 缓存指定 raw auth_key_id + session_id 的业务 auth_key_id。
|
||||
func (m *SessionManager) BindAuthKeyForSession(rawAuthKeyID [8]byte, sessionID int64, authKeyID [8]byte) {
|
||||
m.mu.Lock()
|
||||
|
|
@ -603,18 +466,6 @@ func (m *SessionManager) bindAuthKeyLocked(c *Conn, key sessionKey, authKeyID [8
|
|||
}
|
||||
}
|
||||
|
||||
// AuthKeyID 返回 session 缓存的业务视角 auth_key_id。
|
||||
// ok=false 表示该连接尚未完成 temp→perm 解析。
|
||||
func (m *SessionManager) AuthKeyID(sessionID int64) ([8]byte, bool) {
|
||||
m.mu.RLock()
|
||||
c, _, ok, ambiguous := m.uniqueSessionLocked(sessionID)
|
||||
m.mu.RUnlock()
|
||||
if ambiguous || !ok {
|
||||
return [8]byte{}, false
|
||||
}
|
||||
return c.BusinessAuthKeyID()
|
||||
}
|
||||
|
||||
// AuthKeyIDForSession 返回指定 raw auth_key_id + session_id 缓存的业务 auth_key_id。
|
||||
func (m *SessionManager) AuthKeyIDForSession(rawAuthKeyID [8]byte, sessionID int64) ([8]byte, bool) {
|
||||
m.mu.RLock()
|
||||
|
|
@ -863,28 +714,6 @@ func (m *SessionManager) UnbindAuthKey(authKeyID [8]byte) int {
|
|||
return count
|
||||
}
|
||||
|
||||
// SetReceivesUpdates 标记 session 是否已完成 updates 同步入口。
|
||||
//
|
||||
// TDesktop 登录后会先调用 updates.getState/getDifference 建立本地同步基线。
|
||||
// 在此之前收到的主动 updates 先暂存,待 session 可接收后再异步下发。
|
||||
func (m *SessionManager) SetReceivesUpdates(sessionID int64, receives bool) {
|
||||
m.mu.Lock()
|
||||
c, key, ok, ambiguous := m.uniqueSessionLocked(sessionID)
|
||||
if ambiguous || !ok {
|
||||
if ambiguous {
|
||||
m.log.Warn("Skip SetReceivesUpdates for ambiguous session_id", zap.Int64("session_id", sessionID))
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
}
|
||||
owner, start := m.setReceivesUpdatesLocked(c, key, receives)
|
||||
m.mu.Unlock()
|
||||
|
||||
if start {
|
||||
go m.runFlush(c, key, owner, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// setReceivesUpdatesLocked 是置位/复位的共同内核,调用方须持有 m.mu。
|
||||
// 置位且有暂存时不立即置 receivesUpdates:标记 flushing 并返回该批暂存所属的 userID,
|
||||
// 交由 runFlush 排空后原子置位,期间新到推送继续进 pending,保证暂存与实时推送的
|
||||
|
|
@ -1059,26 +888,6 @@ func (m *SessionManager) SetReceivesUpdatesForAuthKey(authKeyID [8]byte, session
|
|||
}
|
||||
}
|
||||
|
||||
// PushToSession 向指定 session 推送一条消息。
|
||||
func (m *SessionManager) PushToSession(ctx context.Context, sessionID int64, t proto.MessageType, msg bin.Encoder) error {
|
||||
m.mu.RLock()
|
||||
c, key, ok, ambiguous := m.uniqueSessionLocked(sessionID)
|
||||
if ambiguous {
|
||||
m.mu.RUnlock()
|
||||
return ErrSessionAmbiguous
|
||||
}
|
||||
if !ok {
|
||||
m.mu.RUnlock()
|
||||
return ErrSessionNotFound
|
||||
}
|
||||
ready := c.receivesUpdates.Load()
|
||||
m.mu.RUnlock()
|
||||
if ready {
|
||||
return c.Send(ctx, t, msg)
|
||||
}
|
||||
return m.queueOrSendPrepared(ctx, key, t, msg)
|
||||
}
|
||||
|
||||
// PushToSessionForAuthKey 向指定 raw auth_key_id + session_id 推送一条消息。
|
||||
func (m *SessionManager) PushToSessionForAuthKey(ctx context.Context, authKeyID [8]byte, sessionID int64, t proto.MessageType, msg bin.Encoder) error {
|
||||
m.mu.RLock()
|
||||
|
|
@ -1134,18 +943,6 @@ func (m *SessionManager) PushToSessionForAuthKeyImmediate(ctx context.Context, a
|
|||
return c.SendBestEffort(ctx, t, msg, 2*time.Second)
|
||||
}
|
||||
|
||||
// PushToUser 向某 user 所有活跃连接推送,返回已发送或已暂存的连接数。
|
||||
// 发送在释放锁后进行,避免持锁阻塞于网络 IO。
|
||||
func (m *SessionManager) PushToUser(ctx context.Context, userID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
|
||||
return m.PushToUserExceptAuthKeySession(ctx, userID, [8]byte{}, 0, t, msg)
|
||||
}
|
||||
|
||||
// PushToUserExceptSession 向某 user 所有活跃连接推送,但跳过指定 session。
|
||||
// 未完成 updates 同步入口的 session 会先暂存,等 SetReceivesUpdates(true) 后再发。
|
||||
func (m *SessionManager) PushToUserExceptSession(ctx context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
|
||||
return m.pushToUser(ctx, userID, nil, excludeSessionID, t, msg)
|
||||
}
|
||||
|
||||
// PushToUserExceptAuthKeySession 向某 user 所有活跃连接推送,跳过指定 raw auth_key + session。
|
||||
func (m *SessionManager) PushToUserExceptAuthKeySession(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
|
||||
return m.pushToUser(ctx, userID, &excludeAuthKeyID, excludeSessionID, t, msg)
|
||||
|
|
@ -1275,10 +1072,6 @@ func (m *SessionManager) PushToUserTransientExceptAuthKeySession(ctx context.Con
|
|||
})
|
||||
}
|
||||
|
||||
func (m *SessionManager) PushToUserExceptSessionBestEffort(ctx context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder, timeout time.Duration) (int, error) {
|
||||
return m.pushToUserBestEffort(ctx, userID, nil, excludeSessionID, t, msg, timeout)
|
||||
}
|
||||
|
||||
func (m *SessionManager) PushToUserExceptAuthKeySessionBestEffort(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder, timeout time.Duration) (int, error) {
|
||||
return m.pushToUserBestEffort(ctx, userID, &excludeAuthKeyID, excludeSessionID, t, msg, timeout)
|
||||
}
|
||||
|
|
@ -1490,13 +1283,6 @@ func (m *SessionManager) pushToUserWithSender(ctx context.Context, userID int64,
|
|||
return sent + queued, firstErr
|
||||
}
|
||||
|
||||
// Online 返回当前活跃连接数。
|
||||
func (m *SessionManager) Online() int {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return len(m.bySession)
|
||||
}
|
||||
|
||||
// ActiveRawAuthKeyIDs 返回当前物理连接实际使用的 raw auth_key_id 去重快照。
|
||||
// maintenance 用它保护“已建 key 但尚未登录”的长连接不被 orphan GC 删除;不能用
|
||||
// business/temp→perm key 替代,否则活跃 temp 连接仍可能误删。
|
||||
|
|
@ -1757,25 +1543,6 @@ func (m *SessionManager) OnlineChannelIDsSnapshot() []int64 {
|
|||
return out
|
||||
}
|
||||
|
||||
// OnlineChannelIDsAfter is retained for bounded diagnostics/tests. Production recovery takes one
|
||||
// OnlineChannelIDsSnapshot per generation and slices it into pages, avoiding repeated full scans.
|
||||
func (m *SessionManager) OnlineChannelIDsAfter(afterChannelID int64, limit int) []int64 {
|
||||
if limit <= 0 {
|
||||
return nil
|
||||
}
|
||||
const maxRecoveryPage = 4096
|
||||
if limit > maxRecoveryPage {
|
||||
limit = maxRecoveryPage
|
||||
}
|
||||
all := m.OnlineChannelIDsSnapshot()
|
||||
start := sort.Search(len(all), func(i int) bool { return all[i] > afterChannelID })
|
||||
end := start + limit
|
||||
if end > len(all) {
|
||||
end = len(all)
|
||||
}
|
||||
return all[start:end]
|
||||
}
|
||||
|
||||
func (m *SessionManager) onlineChannelUsers(index map[int64]map[sessionKey]int64, channelID int64, limit int) []int64 {
|
||||
if channelID == 0 {
|
||||
return nil
|
||||
|
|
@ -1813,7 +1580,6 @@ func (m *SessionManager) removeLocked(c *Conn, dropPending bool) int64 {
|
|||
return 0
|
||||
}
|
||||
delete(m.bySession, key)
|
||||
removeSessionIDIndex(m.bySessionID, c.sessionID, c.authKeyID)
|
||||
removeConnIndex(m.byAuthKey, c.authKeyID, c.sessionID)
|
||||
if businessAuthKeyID, resolved := c.BusinessAuthKeyID(); resolved {
|
||||
removeBusinessAuthKeyIndex(m.byBusinessAuthKey, businessAuthKeyID, key)
|
||||
|
|
@ -2100,44 +1866,6 @@ func (m *SessionManager) queuePreparedLocked(key sessionKey, t proto.MessageType
|
|||
return true
|
||||
}
|
||||
|
||||
// queueLocked remains as a test/internal single-target convenience. Production fan-out prepares
|
||||
// outside m.mu and calls queuePreparedLocked so TL encoding never serializes the session registry.
|
||||
func (m *SessionManager) queueLocked(key sessionKey, t proto.MessageType, msg bin.Encoder) bool {
|
||||
encoded, reservation, err := m.preparePendingPush(context.Background(), msg)
|
||||
if err != nil {
|
||||
m.log.Debug("Drop pending push outside byte budget",
|
||||
zap.String("auth_key_id", sessionKeyLog(key.authKeyID)),
|
||||
zap.Int64("session_id", key.sessionID),
|
||||
zap.Error(err),
|
||||
)
|
||||
return false
|
||||
}
|
||||
defer reservation.release()
|
||||
return m.queuePreparedLocked(key, t, encoded, reservation)
|
||||
}
|
||||
|
||||
func (m *SessionManager) uniqueSessionLocked(sessionID int64) (*Conn, sessionKey, bool, bool) {
|
||||
set := m.bySessionID[sessionID]
|
||||
if len(set) == 0 {
|
||||
return nil, sessionKey{}, false, false
|
||||
}
|
||||
if len(set) > 1 {
|
||||
return nil, sessionKey{}, false, true
|
||||
}
|
||||
for authKeyID, c := range set {
|
||||
return c, sessionKey{authKeyID: authKeyID, sessionID: sessionID}, true, false
|
||||
}
|
||||
return nil, sessionKey{}, false, false
|
||||
}
|
||||
|
||||
func (m *SessionManager) dropPendingBySessionLocked(sessionID int64) {
|
||||
for key := range m.pending {
|
||||
if key.sessionID == sessionID {
|
||||
m.deletePendingLocked(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RunPendingSweeper 周期回收长期滞留的 pending 暂存:被动老化(queueLocked/takePendingLocked)
|
||||
// 只在「有新推送」或「就绪后取出」时触发,对「已注册但迟迟不调 getState、又恰好没有新推送、
|
||||
// 也不断连(持续 ping 保活)」的连接无法回收其超龄 pending。本 sweeper 给出一个主动兜底,
|
||||
|
|
@ -2236,24 +1964,6 @@ func removeBusinessAuthKeyIndex(idx map[[8]byte]map[sessionKey]*Conn, authKeyID
|
|||
}
|
||||
}
|
||||
|
||||
func addSessionIDIndex(idx map[int64]map[[8]byte]*Conn, sessionID int64, authKeyID [8]byte, c *Conn) {
|
||||
set := idx[sessionID]
|
||||
if set == nil {
|
||||
set = make(map[[8]byte]*Conn)
|
||||
idx[sessionID] = set
|
||||
}
|
||||
set[authKeyID] = c
|
||||
}
|
||||
|
||||
func removeSessionIDIndex(idx map[int64]map[[8]byte]*Conn, sessionID int64, authKeyID [8]byte) {
|
||||
if set := idx[sessionID]; set != nil {
|
||||
delete(set, authKeyID)
|
||||
if len(set) == 0 {
|
||||
delete(idx, sessionID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addUserIndex(idx map[int64]map[sessionKey]*Conn, userID int64, key sessionKey, c *Conn) {
|
||||
set := idx[userID]
|
||||
if set == nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue