feat: add redis cache and fanout support

This commit is contained in:
richjohnrambo
2026-08-25 20:54:03 +08:00
parent 07376c70e3
commit f900a08a01
68 changed files with 3949 additions and 5696 deletions
+63
View File
@@ -205,6 +205,32 @@ func (s *Session) getSub(topic string) *Subscription {
return s.subs[topic]
}
func (s *Session) hasSub(topic string) bool {
if s.multi != nil {
return s.multi.hasSub(topic)
}
s.subsLock.RLock()
defer s.subsLock.RUnlock()
_, ok := s.subs[topic]
return ok
}
func (s *Session) groupSubs() []string {
if s.multi != nil {
return s.multi.groupSubs()
}
s.subsLock.RLock()
defer s.subsLock.RUnlock()
topics := make([]string, 0, len(s.subs))
for topic := range s.subs {
if len(topic) >= 3 && types.GetTopicCat(topic) == types.TopicCatGrp {
topics = append(topics, topic)
}
}
return topics
}
func (s *Session) delSub(topic string) {
if s.multi != nil {
s.multi.delSub(topic)
@@ -434,6 +460,11 @@ func (s *Session) purgeChannels() {
// cleanUp is called when the session is terminated to perform resource cleanup.
func (s *Session) cleanUp(expired bool) {
atomic.StoreInt32(&s.terminating, 1)
if !s.uid.IsZero() && s.tenantID.IsValid() {
if err := store.MarkUserOffline(s.tenantID, s.uid, s.sid, currentNodeID()); err != nil {
logs.Warn.Println("s.cleanUp: failed to mark user offline", s.uid, err, s.sid)
}
}
s.purgeChannels()
s.inflightReqs.Wait()
s.inflightReqs = nil
@@ -488,6 +519,16 @@ func (s *Session) dispatchRaw(raw []byte) {
func (s *Session) dispatch(msg *ClientComMessage) {
now := types.TimeNow()
atomic.StoreInt64(&s.lastAction, now.UnixNano())
if !s.uid.IsZero() && s.tenantID.IsValid() {
if err := store.RefreshUserPresence(s.tenantID, s.uid, s.sid, currentNodeID()); err != nil {
logs.Warn.Println("s.dispatch: failed to refresh user presence", s.uid, err, s.sid)
}
for _, topic := range s.groupSubs() {
if err := store.MarkTopicMemberOnline(s.tenantID, topic, s.uid, currentNodeID()); err != nil {
logs.Warn.Println("s.dispatch: failed to refresh group member presence", topic, s.uid, err, s.sid)
}
}
}
// This should be the first block here, before any other checks.
var resp *ServerComMessage
@@ -1200,6 +1241,9 @@ func (s *Session) onLogin(msgID string, timestamp time.Time, rec *auth.Rec, miss
// Authenticate the session.
s.uid = rec.Uid
s.authLvl = rec.AuthLevel
if err := store.MarkUserOnline(s.tenantID, s.uid, s.sid, currentNodeID(), s.platf, s.userAgent); err != nil {
logs.Warn.Println("s.onLogin: failed to mark user online", s.uid, err, s.sid)
}
// Reset expiration time.
rec.Lifetime = 0
}
@@ -1228,6 +1272,25 @@ func (s *Session) onLogin(msgID string, timestamp time.Time, rec *auth.Rec, miss
return reply
}
func currentNodeID() string {
if globals.cluster != nil && globals.cluster.thisNodeName != "" {
return globals.cluster.thisNodeName
}
return "local"
}
func redisUserOnline(tenantID types.TenantID, uid types.Uid, fallback bool) bool {
if !store.UserPresenceEnabled() || !tenantID.IsValid() || uid.IsZero() {
return fallback
}
presence, err := store.GetUserPresence(tenantID, uid)
if err != nil {
logs.Warn.Printf("presence[%s]: failed to read redis presence: %v", uid.UserId(), err)
return fallback
}
return presence.Online
}
func (s *Session) get(msg *ClientComMessage) {
// Expand topic name.
var resp *ServerComMessage