github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/scrape/manager.go (about)

     1  // Copyright 2013 The Prometheus Authors
     2  // Copyright 2021 The Pyroscope Authors
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  // http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package scrape
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  	"reflect"
    22  	"sync"
    23  
    24  	"github.com/prometheus/client_golang/prometheus"
    25  	"github.com/sirupsen/logrus"
    26  
    27  	"github.com/pyroscope-io/pyroscope/pkg/ingestion"
    28  	"github.com/pyroscope-io/pyroscope/pkg/scrape/config"
    29  	"github.com/pyroscope-io/pyroscope/pkg/scrape/discovery/targetgroup"
    30  )
    31  
    32  // Manager maintains a set of scrape pools and manages start/stop cycles
    33  // when receiving new target groups from the discovery manager.
    34  type Manager struct {
    35  	logger   logrus.FieldLogger
    36  	ingester ingestion.Ingester
    37  	stop     chan struct{}
    38  
    39  	*metrics
    40  	jitterSeed uint64     // Global jitterSeed seed is used to spread scrape workload across HA setup.
    41  	mtxScrape  sync.Mutex // Guards the fields below.
    42  
    43  	scrapeConfigs map[string]*config.Config
    44  	scrapePools   map[string]*scrapePool
    45  	targetSets    map[string][]*targetgroup.Group
    46  
    47  	reloadC chan struct{}
    48  }
    49  
    50  // NewManager is the Manager constructor
    51  func NewManager(logger logrus.FieldLogger, p ingestion.Ingester, r prometheus.Registerer) *Manager {
    52  	c := make(map[string]*config.Config)
    53  	return &Manager{
    54  		ingester:      p,
    55  		logger:        logger,
    56  		scrapeConfigs: c,
    57  		scrapePools:   make(map[string]*scrapePool),
    58  		stop:          make(chan struct{}),
    59  		reloadC:       make(chan struct{}, 1),
    60  		metrics:       newMetrics(r),
    61  	}
    62  }
    63  
    64  // Run receives and saves target set updates and triggers the scraping loops reloading.
    65  // Reloading happens in the background so that it doesn't block receiving targets updates.
    66  func (m *Manager) Run(tsets <-chan map[string][]*targetgroup.Group) error {
    67  	m.reload()
    68  	for {
    69  		select {
    70  		case ts := <-tsets:
    71  			m.mtxScrape.Lock()
    72  			m.targetSets = ts
    73  			m.mtxScrape.Unlock()
    74  			m.reload()
    75  		case <-m.stop:
    76  			return nil
    77  		}
    78  	}
    79  }
    80  
    81  func (m *Manager) reload() {
    82  	m.mtxScrape.Lock()
    83  	var wg sync.WaitGroup
    84  	for setName, groups := range m.targetSets {
    85  		if _, ok := m.scrapePools[setName]; !ok {
    86  			scrapeConfig, ok := m.scrapeConfigs[setName]
    87  			if !ok {
    88  				m.logger.WithError(fmt.Errorf("invalid config id: %s", setName)).
    89  					WithField("scrape_pool", setName).
    90  					Errorf("reloading target set")
    91  				continue
    92  			}
    93  			sp, err := newScrapePool(scrapeConfig, m.ingester, m.logger, m.metrics)
    94  			if err != nil {
    95  				m.logger.WithError(err).
    96  					WithField("scrape_pool", setName).
    97  					Errorf("creating new scrape pool")
    98  				continue
    99  			}
   100  			m.scrapePools[setName] = sp
   101  		}
   102  
   103  		wg.Add(1)
   104  		// Run the sync in parallel as these take a while and at high load can't catch up.
   105  		go func(sp *scrapePool, groups []*targetgroup.Group) {
   106  			sp.Sync(groups)
   107  			wg.Done()
   108  		}(m.scrapePools[setName], groups)
   109  	}
   110  	m.mtxScrape.Unlock()
   111  	wg.Wait()
   112  }
   113  
   114  // Stop cancels all running scrape pools and blocks until all have exited.
   115  func (m *Manager) Stop() {
   116  	m.mtxScrape.Lock()
   117  	defer m.mtxScrape.Unlock()
   118  	for _, sp := range m.scrapePools {
   119  		sp.stop()
   120  	}
   121  	close(m.stop)
   122  }
   123  
   124  // ApplyConfig resets the manager's target providers and job configurations as defined by the new cfg.
   125  func (m *Manager) ApplyConfig(cfg []*config.Config) error {
   126  	m.mtxScrape.Lock()
   127  	defer m.mtxScrape.Unlock()
   128  	c := make(map[string]*config.Config)
   129  	for _, x := range cfg {
   130  		c[x.JobName] = x
   131  	}
   132  	m.scrapeConfigs = c
   133  	// Cleanup and reload pool if the configuration has changed.
   134  	var failed bool
   135  	for name, sp := range m.scrapePools {
   136  		cfg, ok := m.scrapeConfigs[name]
   137  		if !ok {
   138  			sp.stop()
   139  			delete(m.scrapePools, name)
   140  			continue
   141  		}
   142  		if reflect.DeepEqual(sp.config, cfg) {
   143  			continue
   144  		}
   145  		if err := sp.reload(cfg); err != nil {
   146  			m.logger.WithError(err).Errorf("reloading scrape pool")
   147  			failed = true
   148  		}
   149  	}
   150  
   151  	if failed {
   152  		return errors.New("failed to apply the new configuration")
   153  	}
   154  	return nil
   155  }
   156  
   157  // TargetsAll returns active and dropped targets grouped by job_name.
   158  func (m *Manager) TargetsAll() map[string][]*Target {
   159  	m.mtxScrape.Lock()
   160  	defer m.mtxScrape.Unlock()
   161  	targets := make(map[string][]*Target, len(m.scrapePools))
   162  	for tset, sp := range m.scrapePools {
   163  		targets[tset] = append(sp.ActiveTargets(), sp.DroppedTargets()...)
   164  	}
   165  	return targets
   166  }
   167  
   168  // TargetsActive returns the active targets currently being scraped.
   169  func (m *Manager) TargetsActive() map[string][]*Target {
   170  	m.mtxScrape.Lock()
   171  	defer m.mtxScrape.Unlock()
   172  
   173  	var (
   174  		wg  sync.WaitGroup
   175  		mtx sync.Mutex
   176  	)
   177  
   178  	targets := make(map[string][]*Target, len(m.scrapePools))
   179  	wg.Add(len(m.scrapePools))
   180  	for tset, sp := range m.scrapePools {
   181  		// Running in parallel limits the blocking time of scrapePool to scrape
   182  		// interval when there's an update from SD.
   183  		go func(tset string, sp *scrapePool) {
   184  			mtx.Lock()
   185  			targets[tset] = sp.ActiveTargets()
   186  			mtx.Unlock()
   187  			wg.Done()
   188  		}(tset, sp)
   189  	}
   190  	wg.Wait()
   191  	return targets
   192  }
   193  
   194  // TargetsDropped returns the dropped targets during relabelling.
   195  func (m *Manager) TargetsDropped() map[string][]*Target {
   196  	m.mtxScrape.Lock()
   197  	defer m.mtxScrape.Unlock()
   198  
   199  	targets := make(map[string][]*Target, len(m.scrapePools))
   200  	for tset, sp := range m.scrapePools {
   201  		targets[tset] = sp.DroppedTargets()
   202  	}
   203  	return targets
   204  }