2026-06-26 23:21:22 +08:00

85 lines
2.3 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 (
"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(records []outbox.Record) []outbox.Record {
if len(records) == 0 || s == nil || s.robotDisplaySampler == nil {
return records
}
return s.robotDisplaySampler.filter(records, robotDisplaySampleWindow)
}
func (s *robotDisplaySampler) filter(records []outbox.Record, window time.Duration) []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{
appCode: appcode.Normalize(record.AppCode),
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)
}
}
}