func (watcher *Watcher) Run(signals <-chan os.Signal, ready chan<- struct{}) error { logger := watcher.logger.Session("watcher") logger.Info("starting") defer logger.Info("finished") var subscription events.EventSource subscriptionChan := make(chan events.EventSource, 1) go subscribeToEvents(logger, watcher.bbsClient, subscriptionChan) eventChan := make(chan models.Event, 1) nextErrCount := 0 close(ready) logger.Info("started") for { select { case subscription = <-subscriptionChan: if subscription != nil { go nextEvent(logger, subscription, eventChan) } else { go subscribeToEvents(logger, watcher.bbsClient, subscriptionChan) } case event := <-eventChan: if event != nil { watcher.handleEvent(logger, event) } else { nextErrCount += 1 if nextErrCount > 2 { nextErrCount = 0 go subscribeToEvents(logger, watcher.bbsClient, subscriptionChan) break } } go nextEvent(logger, subscription, eventChan) case <-signals: logger.Info("stopping") err := subscription.Close() if err != nil { logger.Error("failed-closing-event-source", err) } return nil } } }
var event models.Event for { Eventually(eventChannel).Should(Receive(&event)) if event.EventType() == models.EventTypeDesiredLRPRemoved { break } } }) AfterEach(func() { Eventually(done).Should(BeClosed()) ginkgomon.Kill(bbsProcess) }) It("does not emit latency metrics", func() { eventSource.Close() timeout := time.After(50 * time.Millisecond) for { select { case envelope := <-testMetricsChan: if envelope.GetEventType() == sonde_events.Envelope_ValueMetric { Expect(*envelope.ValueMetric.Name).NotTo(Equal("RequestLatency")) } case <-timeout: return } } }) It("emits request counting metrics", func() {
func (h *EventStreamHandler) EventStream(w http.ResponseWriter, req *http.Request) { logger := h.logger.Session("event-stream-handler") closeNotifier := w.(http.CloseNotifier).CloseNotify() sourceChan := make(chan events.EventSource) flusher := w.(http.Flusher) go func() { source, err := h.bbs.SubscribeToEvents() if err != nil { logger.Error("failed-to-subscribe-to-events", err) close(sourceChan) return } sourceChan <- source }() var source events.EventSource select { case source = <-sourceChan: if source == nil { w.WriteHeader(http.StatusInternalServerError) return } case <-closeNotifier: return } defer source.Close() go func() { <-closeNotifier source.Close() }() w.Header().Add("Content-Type", "text/event-stream; charset=utf-8") w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate") w.Header().Add("Connection", "keep-alive") w.WriteHeader(http.StatusOK) flusher.Flush() eventID := 0 for { bbsEvent, err := source.Next() if err != nil { logger.Error("failed-to-get-next-event", err) return } event, err := NewEventFromBBS(bbsEvent) if err != nil { logger.Error("failed-to-marshal-event", err) return } payload, err := json.Marshal(event) if err != nil { logger.Error("failed-to-marshal-event", err) return } err = sse.Event{ ID: strconv.Itoa(eventID), Name: string(event.EventType()), Data: payload, }.Write(w) if err != nil { break } flusher.Flush() eventID++ } }