sweepIdleStreams iterates chatStreams once and delegates each entry to cleanupStreamIfIdle. Range may skip entries that become reapable concurrently. Any such entry is reaped on the next tick.
()
| 4780 | // to cleanupStreamIfIdle. Range may skip entries that become reapable |
| 4781 | // concurrently. Any such entry is reaped on the next tick. |
| 4782 | func (p *Server) sweepIdleStreams() { |
| 4783 | var reaped atomic.Int64 |
| 4784 | defer func() { |
| 4785 | if count := reaped.Load(); count > 0 { |
| 4786 | p.logger.Info(context.Background(), "reaped idle chat streams", slog.F("count", count)) |
| 4787 | } |
| 4788 | }() |
| 4789 | p.chatStreams.Range(func(key, value any) bool { |
| 4790 | chatID, ok := key.(uuid.UUID) |
| 4791 | if !ok { |
| 4792 | return true |
| 4793 | } |
| 4794 | state, ok := value.(*chatStreamState) |
| 4795 | if !ok { |
| 4796 | return true |
| 4797 | } |
| 4798 | // guard against any panic from cleanupStreamIfIdle locking state.mu for all time |
| 4799 | func() { |
| 4800 | state.mu.Lock() |
| 4801 | defer state.mu.Unlock() |
| 4802 | if p.cleanupStreamIfIdle(chatID, state) { |
| 4803 | reaped.Add(1) |
| 4804 | } |
| 4805 | }() |
| 4806 | return true |
| 4807 | }) |
| 4808 | } |
| 4809 | |
| 4810 | // registerHeartbeat enrolls a chat in the centralized batch |
| 4811 | // heartbeat loop. Must be called after chatCtx is created. |