func TestScrapePoolStop(t *testing.T) { sp := &scrapePool{ targets: map[uint64]*Target{}, loops: map[uint64]loop{}, } var mtx sync.Mutex stopped := map[uint64]bool{} numTargets := 20 // Stopping the scrape pool must call stop() on all scrape loops, // clean them and the respective targets up. It must wait until each loop's // stop function returned before returning itself. for i := 0; i < numTargets; i++ { t := &Target{ labels: model.LabelSet{ model.AddressLabel: model.LabelValue(fmt.Sprintf("example.com:%d", i)), }, } l := &testLoop{} l.stopFunc = func() { time.Sleep(time.Duration(i*20) * time.Millisecond) mtx.Lock() stopped[t.hash()] = true mtx.Unlock() } sp.targets[t.hash()] = t sp.loops[t.hash()] = l } done := make(chan struct{}) stopTime := time.Now() go func() { sp.stop() close(done) }() select { case <-time.After(5 * time.Second): t.Fatalf("scrapeLoop.stop() did not return as expected") case <-done: // This should have taken at least as long as the last target slept. if time.Since(stopTime) < time.Duration(numTargets*20)*time.Millisecond { t.Fatalf("scrapeLoop.stop() exited before all targets stopped") } } mtx.Lock() if len(stopped) != numTargets { t.Fatalf("Expected 20 stopped loops, got %d", len(stopped)) } mtx.Unlock() if len(sp.targets) > 0 { t.Fatalf("Targets were not cleared on stopping: %d left", len(sp.targets)) } if len(sp.loops) > 0 { t.Fatalf("Loops were not cleared on stopping: %d left", len(sp.loops)) } }
func TestScrapePoolReload(t *testing.T) { var mtx sync.Mutex numTargets := 20 stopped := map[uint64]bool{} reloadCfg := &config.ScrapeConfig{ ScrapeInterval: model.Duration(3 * time.Second), ScrapeTimeout: model.Duration(2 * time.Second), } // On starting to run, new loops created on reload check whether their preceeding // equivalents have been stopped. newLoop := func(ctx context.Context, s scraper, app, reportApp storage.SampleAppender) loop { l := &testLoop{} l.startFunc = func(interval, timeout time.Duration, errc chan<- error) { if interval != 3*time.Second { t.Errorf("Expected scrape interval %d but got %d", 3*time.Second, interval) } if timeout != 2*time.Second { t.Errorf("Expected scrape timeout %d but got %d", 2*time.Second, timeout) } mtx.Lock() if !stopped[s.(*targetScraper).hash()] { t.Errorf("Scrape loop for %v not stopped yet", s.(*targetScraper)) } mtx.Unlock() } return l } sp := &scrapePool{ targets: map[uint64]*Target{}, loops: map[uint64]loop{}, newLoop: newLoop, } // Reloading a scrape pool with a new scrape configuration must stop all scrape // loops and start new ones. A new loop must not be started before the preceeding // one terminated. for i := 0; i < numTargets; i++ { t := &Target{ labels: model.LabelSet{ model.AddressLabel: model.LabelValue(fmt.Sprintf("example.com:%d", i)), }, } l := &testLoop{} l.stopFunc = func() { time.Sleep(time.Duration(i*20) * time.Millisecond) mtx.Lock() stopped[t.hash()] = true mtx.Unlock() } sp.targets[t.hash()] = t sp.loops[t.hash()] = l } done := make(chan struct{}) beforeTargets := map[uint64]*Target{} for h, t := range sp.targets { beforeTargets[h] = t } reloadTime := time.Now() go func() { sp.reload(reloadCfg) close(done) }() select { case <-time.After(5 * time.Second): t.Fatalf("scrapeLoop.reload() did not return as expected") case <-done: // This should have taken at least as long as the last target slept. if time.Since(reloadTime) < time.Duration(numTargets*20)*time.Millisecond { t.Fatalf("scrapeLoop.stop() exited before all targets stopped") } } mtx.Lock() if len(stopped) != numTargets { t.Fatalf("Expected 20 stopped loops, got %d", len(stopped)) } mtx.Unlock() if !reflect.DeepEqual(sp.targets, beforeTargets) { t.Fatalf("Reloading affected target states unexpectedly") } if len(sp.loops) != numTargets { t.Fatalf("Expected %d loops after reload but got %d", numTargets, len(sp.loops)) } }