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 }