Shutdown gracefully shuts down the stream, sending any supplementary events downstream if required. ONLY call this once all events have been submitted.
(shutdownCtx context.Context)
| 193 | // Shutdown gracefully shuts down the stream, sending any supplementary events downstream if required. |
| 194 | // ONLY call this once all events have been submitted. |
| 195 | func (s *EventStream) Shutdown(shutdownCtx context.Context) error { |
| 196 | s.shutdownOnce.Do(func() { |
| 197 | s.logger.Debug(shutdownCtx, "shutdown initiated", slog.F("outstanding_events", len(s.eventsCh))) |
| 198 | |
| 199 | // Now it is safe to close the events channel; the Start() loop will exit |
| 200 | // after draining remaining events and receivers will stop ranging. |
| 201 | close(s.eventsCh) |
| 202 | }) |
| 203 | |
| 204 | var err error |
| 205 | select { |
| 206 | case <-shutdownCtx.Done(): |
| 207 | // If shutdownCtx completes, shutdown likely exceeded its timeout. |
| 208 | err = xerrors.Errorf("shutdown ended prematurely with %d outstanding events: %w", len(s.eventsCh), shutdownCtx.Err()) |
| 209 | case <-s.ctx.Done(): |
| 210 | err = xerrors.Errorf("shutdown ended prematurely with %d outstanding events: %w", len(s.eventsCh), s.ctx.Err()) |
| 211 | case <-s.doneCh: |
| 212 | return nil |
| 213 | } |
| 214 | |
| 215 | // Even if the context is canceled, we need to wait for Start() to complete. |
| 216 | <-s.doneCh |
| 217 | return err |
| 218 | } |
| 219 | |
| 220 | // IsStreaming checks if the stream has been initiated, or |
| 221 | // when events are buffered which - when processed - will initiate the stream. |