perf: batch group unread fanout
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
const (
|
||||
realtimeKindMessage = "message"
|
||||
realtimeKindGroupUnread = "group_unread"
|
||||
realtimeKindSessionControl = "session_control"
|
||||
realtimeKindCallEvent = "call_event"
|
||||
|
||||
@@ -23,8 +24,21 @@ const (
|
||||
realtimeTargetUser = "user"
|
||||
realtimeTargetDevice = "device"
|
||||
realtimeTargetPlatf = "platform"
|
||||
realtimeTargetUsers = "users"
|
||||
)
|
||||
|
||||
type realtimeGroupUnreadRecipient struct {
|
||||
User string `json:"user"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
type realtimeGroupUnreadMessage struct {
|
||||
Topic string `json:"topic"`
|
||||
Actor string `json:"actor,omitempty"`
|
||||
SeqID int64 `json:"seq_id"`
|
||||
Recipients []realtimeGroupUnreadRecipient `json:"recipients"`
|
||||
}
|
||||
|
||||
type realtimeCallEventMessage struct {
|
||||
Topic string `json:"topic"`
|
||||
From string `json:"from"`
|
||||
@@ -68,6 +82,12 @@ func publishRealtimeUserMessage(uid types.Uid, msg *ServerComMessage) {
|
||||
immetrics.IncRealtimeFanout("publish", realtimeKindMessage, realtimeTargetUser, "skip_invalid")
|
||||
return
|
||||
}
|
||||
// Native cluster routing already forwards user topics between pods. Publishing
|
||||
// the same message through Redis duplicates work and may deliver it twice.
|
||||
if globals.cluster != nil {
|
||||
immetrics.IncRealtimeFanout("publish", realtimeKindMessage, realtimeTargetUser, "skip_native_cluster")
|
||||
return
|
||||
}
|
||||
dedupKey := realtimeMessageDedupKey(uid.UserId(), msg)
|
||||
if err := store.PublishRealtimeFanout(currentNodeID(), msg.TenantID, realtimeKindMessage, realtimeTargetUser, uid.UserId(), msg.SkipSid, dedupKey, msg); err != nil {
|
||||
immetrics.IncRealtimeFanout("publish", realtimeKindMessage, realtimeTargetUser, "error")
|
||||
@@ -77,6 +97,29 @@ func publishRealtimeUserMessage(uid types.Uid, msg *ServerComMessage) {
|
||||
immetrics.IncRealtimeFanout("publish", realtimeKindMessage, realtimeTargetUser, "ok")
|
||||
}
|
||||
|
||||
func publishRealtimeGroupUnread(tenantID types.TenantID, topic, actor string, seqID int64,
|
||||
recipients []realtimeGroupUnreadRecipient) bool {
|
||||
if !tenantID.IsValid() || topic == "" || seqID <= 0 || len(recipients) == 0 || !store.RealtimeFanoutEnabled() {
|
||||
return false
|
||||
}
|
||||
payload := &realtimeGroupUnreadMessage{
|
||||
Topic: topic,
|
||||
Actor: actor,
|
||||
SeqID: seqID,
|
||||
Recipients: recipients,
|
||||
}
|
||||
dedupKey := "group-unread:" + topic + ":" + strconv.FormatInt(seqID, 10)
|
||||
if err := store.PublishRealtimeFanout(currentNodeID(), tenantID, realtimeKindGroupUnread,
|
||||
realtimeTargetUsers, topic, "", dedupKey, payload); err != nil {
|
||||
immetrics.IncRealtimeFanout("publish", realtimeKindGroupUnread, realtimeTargetUsers, "error")
|
||||
logs.Warn.Printf("redis realtime fanout: publish group unread topic=%s seq=%d failed: %v", topic, seqID, err)
|
||||
return false
|
||||
}
|
||||
immetrics.IncRealtimeFanout("publish", realtimeKindGroupUnread, realtimeTargetUsers, "ok")
|
||||
deliverRealtimeGroupUnread(tenantID, payload)
|
||||
return true
|
||||
}
|
||||
|
||||
func publishRealtimeCallEvent(msg *ClientComMessage) {
|
||||
if msg == nil || msg.Note == nil || !msg.TenantID.IsValid() ||
|
||||
msg.RcptTo == "" || msg.AsUser == "" || msg.Note.What != "call" ||
|
||||
@@ -193,6 +236,16 @@ func deliverRealtimeFanoutEnvelope(envelope *store.RealtimeFanoutEnvelope) {
|
||||
return
|
||||
}
|
||||
immetrics.IncRealtimeFanout("receive", envelope.Kind, envelope.TargetType, "ok")
|
||||
if envelope.Kind == realtimeKindGroupUnread {
|
||||
var batch realtimeGroupUnreadMessage
|
||||
if err := json.Unmarshal(envelope.Message, &batch); err != nil {
|
||||
immetrics.IncRealtimeFanout("receive", envelope.Kind, envelope.TargetType, "malformed")
|
||||
logs.Warn.Println("redis realtime fanout: malformed group unread batch", err)
|
||||
return
|
||||
}
|
||||
deliverRealtimeGroupUnread(envelope.TenantID, &batch)
|
||||
return
|
||||
}
|
||||
var msg ServerComMessage
|
||||
if err := json.Unmarshal(envelope.Message, &msg); err != nil {
|
||||
immetrics.IncRealtimeFanout("receive", envelope.Kind, envelope.TargetType, "malformed")
|
||||
@@ -224,6 +277,51 @@ func deliverRealtimeFanoutEnvelope(envelope *store.RealtimeFanoutEnvelope) {
|
||||
}
|
||||
}
|
||||
|
||||
func deliverRealtimeGroupUnread(tenantID types.TenantID, batch *realtimeGroupUnreadMessage) {
|
||||
if globals.sessionStore == nil || batch == nil || batch.Topic == "" || batch.SeqID <= 0 || len(batch.Recipients) == 0 {
|
||||
immetrics.IncRealtimeFanout("deliver", realtimeKindGroupUnread, realtimeTargetUsers, "skip_invalid")
|
||||
return
|
||||
}
|
||||
recipients := make(map[types.Uid]string, len(batch.Recipients))
|
||||
for _, recipient := range batch.Recipients {
|
||||
uid := types.ParseUserId(recipient.User)
|
||||
if !uid.IsZero() {
|
||||
recipients[uid] = recipient.Source
|
||||
}
|
||||
}
|
||||
delivered := 0
|
||||
globals.sessionStore.Range(func(_ string, sess *Session) bool {
|
||||
if sess == nil || sess.isCluster() || sess.isProxy() || sess.tenantID != tenantID {
|
||||
return true
|
||||
}
|
||||
source, ok := recipients[sess.uid]
|
||||
if !ok || sessionHasTopicOrP2PAlias(sess, batch.Topic) {
|
||||
return true
|
||||
}
|
||||
actor := batch.Actor
|
||||
if actor == sess.uid.UserId() {
|
||||
actor = ""
|
||||
}
|
||||
msg := &ServerComMessage{
|
||||
TenantID: tenantID,
|
||||
RcptTo: sess.uid.UserId(),
|
||||
Pres: &MsgServerPres{
|
||||
Topic: "me",
|
||||
What: "msg",
|
||||
Src: source,
|
||||
AcsActor: actor,
|
||||
SeqId: batch.SeqID,
|
||||
SkipTopic: batch.Topic,
|
||||
},
|
||||
}
|
||||
if sess.queueOut(msg) {
|
||||
delivered++
|
||||
}
|
||||
return true
|
||||
})
|
||||
recordRealtimeFanoutDeliverResult(realtimeKindGroupUnread, realtimeTargetUsers, delivered)
|
||||
}
|
||||
|
||||
func deliverRealtimeCallEvent(envelope *store.RealtimeFanoutEnvelope) {
|
||||
var event realtimeCallEventMessage
|
||||
if err := json.Unmarshal(envelope.Message, &event); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user