88 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"sync"
"time"
"hyapp/pkg/appcode"
"hyapp/services/room-service/internal/room/outbox"
)
const (
// robotDisplaySampleWindow 是 v1 展示降采样窗口:同房间同事件类型 2 秒内只直发一条机器人展示 IM。
robotDisplaySampleWindow = 2 * time.Second
)
type robotDisplaySampleKey struct {
appCode string
roomID string
eventType string
}
type robotDisplaySampler struct {
mu sync.Mutex
lastWindow map[robotDisplaySampleKey]int64
}
func newRobotDisplaySampler() *robotDisplaySampler {
return &robotDisplaySampler{lastWindow: make(map[robotDisplaySampleKey]int64)}
}
func (s *Service) sampleRobotDisplayRecords(ctx context.Context, records []outbox.Record) []outbox.Record {
if len(records) == 0 || s == nil || s.robotDisplaySampler == nil {
return records
}
return s.robotDisplaySampler.filter(records, robotDisplaySampleWindow, appcode.FromContext(ctx))
}
func (s *robotDisplaySampler) filter(records []outbox.Record, window time.Duration, scopedApp string) []outbox.Record {
if s == nil || window <= 0 || len(records) == 0 {
return records
}
windowMS := window.Milliseconds()
out := make([]outbox.Record, 0, len(records))
s.mu.Lock()
defer s.mu.Unlock()
var newestWindow int64
for _, record := range records {
key := robotDisplaySampleKey{
// Build 阶段故意不猜 App降采样发生在发布边界补值之前因此必须使用当前命令上下文兜底
// 否则不同 App 的同房间标识会共用默认 Lalu 窗口并误丢展示事件。
appCode: appcode.Normalize(firstNonEmpty(record.AppCode, scopedApp)),
roomID: record.RoomID,
eventType: record.EventType,
}
createdAtMS := record.CreatedAtMS
if createdAtMS <= 0 && record.Envelope != nil {
createdAtMS = record.Envelope.GetOccurredAtMs()
}
windowStartMS := createdAtMS / windowMS * windowMS
if windowStartMS > newestWindow {
newestWindow = windowStartMS
}
if last, exists := s.lastWindow[key]; exists && windowStartMS <= last {
// 展示事件过密时只丢机器人 IM 展示Room Cell 命令、房间热度、榜单和火箭状态已经在进入这里前完成计算。
continue
}
s.lastWindow[key] = windowStartMS
out = append(out, record)
}
if newestWindow > 0 && len(s.lastWindow) > 1024 {
// 机器人房数量和事件类型有限,超过阈值说明进程运行很久;保留最近几个窗口即可避免 map 常驻增长。
s.pruneLocked(newestWindow - 4*windowMS)
}
return out
}
func (s *robotDisplaySampler) pruneLocked(beforeWindowMS int64) {
for key, windowStartMS := range s.lastWindow {
if windowStartMS < beforeWindowMS {
delete(s.lastWindow, key)
}
}
}