package service import ( "sync" "time" "hyapp/pkg/appcode" "hyapp/services/room-service/internal/room/outbox" ) const ( // robotOutboxSampleWindow 是 v1 展示降采样窗口:同房间同事件类型 2 秒内只保留一条 robot outbox。 robotOutboxSampleWindow = 2 * time.Second ) type robotOutboxSampleKey struct { appCode string roomID string eventType string } type robotOutboxSampler struct { mu sync.Mutex lastWindow map[robotOutboxSampleKey]int64 } func newRobotOutboxSampler() *robotOutboxSampler { return &robotOutboxSampler{lastWindow: make(map[robotOutboxSampleKey]int64)} } func (s *Service) sampleRobotOutboxRecords(records []outbox.Record) []outbox.Record { if len(records) == 0 || s == nil || s.robotOutboxSampler == nil { return records } return s.robotOutboxSampler.filter(records, robotOutboxSampleWindow) } func (s *robotOutboxSampler) 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 := robotOutboxSampleKey{ 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 { // 展示事件过密时只丢 robot outbox 记录;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 *robotOutboxSampler) pruneLocked(beforeWindowMS int64) { for key, windowStartMS := range s.lastWindow { if windowStartMS < beforeWindowMS { delete(s.lastWindow, key) } } }