@@ -22,6 +22,7 @@ import (
2222 "sync"
2323 "time"
2424
25+ "github.com/cenkalti/backoff/v5"
2526 "github.com/prometheus/client_golang/prometheus"
2627 "github.com/prometheus/common/config"
2728 "github.com/prometheus/common/promslog"
@@ -95,7 +96,7 @@ func NewManager(ctx context.Context, logger *slog.Logger, registerer prometheus.
9596 targets : make (map [poolKey ]map [string ]* targetgroup.Group ),
9697 ctx : ctx ,
9798 updatert : 5 * time .Second ,
98- triggerSend : make (chan struct {}, 1 ),
99+ triggerSend : make (chan struct {}, 1 ), // At least one element to ensure we can do a delayed read.
99100 registerer : registerer ,
100101 sdMetrics : sdMetrics ,
101102 }
@@ -158,17 +159,6 @@ func FeatureRegistry(fr features.Collector) func(*Manager) {
158159 }
159160}
160161
161- // SkipStartupWait configures the manager to skip the initial wait on startup.
162- // This is useful for Prometheus in agent mode or serverless flavours of OTel's prometheusreceiver
163- // which are sensitive to startup latencies.
164- func SkipStartupWait () func (* Manager ) {
165- return func (m * Manager ) {
166- m .mtx .Lock ()
167- defer m .mtx .Unlock ()
168- m .skipStartupWait = true
169- }
170- }
171-
172162// Manager maintains a set of discovery providers and sends each update to a map channel.
173163// Targets are grouped by the target set name.
174164type Manager struct {
@@ -206,11 +196,6 @@ type Manager struct {
206196
207197 // featureRegistry is used to track which service discovery providers are configured.
208198 featureRegistry features.Collector
209-
210- // skipStartupWait allows the discovery manager to skip the initial wait before sending updates
211- // to the channel. This is useful for Prometheus in agent mode or serverless flavours of OTel's prometheusreceiver
212- // which are sensitive to startup latencies.
213- skipStartupWait bool
214199}
215200
216201// Providers returns the currently configured SD providers.
@@ -253,8 +238,6 @@ func (m *Manager) ApplyConfig(cfg map[string]Configs) error {
253238 var (
254239 wg sync.WaitGroup
255240 newProviders []* Provider
256- // triggerSync shows if we should trigger send to notify downstream of changes.
257- triggerSync bool
258241 )
259242 for _ , prov := range m .providers {
260243 // Cancel obsolete providers if it has no new subs and it has a cancel function.
@@ -269,7 +252,6 @@ func (m *Manager) ApplyConfig(cfg map[string]Configs) error {
269252
270253 prov .cancel ()
271254 prov .mu .RUnlock ()
272- triggerSync = true // Trigger send to notify downstream of dropped targets
273255 continue
274256 }
275257 prov .mu .RUnlock ()
@@ -281,7 +263,6 @@ func (m *Manager) ApplyConfig(cfg map[string]Configs) error {
281263
282264 m .targetsMtx .Lock ()
283265 for s := range prov .subs {
284- triggerSync = true // Trigger send because this is an existing provider (reload)
285266 refTargets = m .targets [poolKey {s , prov .name }]
286267 // Remove obsolete subs' targets.
287268 if _ , ok := prov .newSubs [s ]; ! ok {
@@ -314,7 +295,7 @@ func (m *Manager) ApplyConfig(cfg map[string]Configs) error {
314295 // See https://github.com/prometheus/prometheus/pull/8639 for details.
315296 // This also helps making the downstream managers drop stale targets as soon as possible.
316297 // See https://github.com/prometheus/prometheus/pull/13147 for details.
317- if triggerSync {
298+ if len ( m . providers ) > 0 {
318299 select {
319300 case m .triggerSend <- struct {}{}:
320301 default :
@@ -402,59 +383,47 @@ func (m *Manager) updater(ctx context.Context, p *Provider, updates chan []*targ
402383 }
403384}
404385
405- func (m * Manager ) flushUpdates (ctx context.Context , timeout <- chan time.Time ) {
406- m .metrics .SentUpdates .Inc ()
407- select {
408- case m .syncCh <- m .allGroups ():
409- case <- timeout :
410- m .metrics .DelayedUpdates .Inc ()
411- m .logger .Debug ("Discovery receiver's channel was full so will retry the next cycle" )
412- select {
413- case m .triggerSend <- struct {}{}:
414- case <- ctx .Done ():
415- return
416- default :
417- }
418- }
419- }
420-
421386func (m * Manager ) sender () {
422- ticker := time .NewTicker (m .updatert )
423387 defer func () {
424- ticker .Stop ()
425388 close (m .syncCh )
426389 }()
427390
428- if m . skipStartupWait {
429- select {
430- case <- m . triggerSend :
431- m . flushUpdates ( m . ctx , ticker . C )
432- case <- m . ctx . Done ():
433- return
434- }
435- ticker . Reset ( m .updatert )
391+ // Some discoverers send updates too often, so we throttle these with a backoff interval that
392+ // increased the interval up to m.updatert delay.
393+ lastSent := time . Now (). Add ( - 1 * m . updatert )
394+ b := & backoff. ExponentialBackOff {
395+ InitialInterval : 100 * time . Millisecond ,
396+ RandomizationFactor : backoff . DefaultRandomizationFactor ,
397+ Multiplier : backoff . DefaultMultiplier ,
398+ MaxInterval : m .updatert ,
436399 }
437400
438401 for {
439402 select {
440403 case <- m .ctx .Done ():
441404 return
442- case <- ticker . C : // Some discoverers send updates too often, so we throttle these with the ticker.
405+ case <- time . After ( b . NextBackOff ()):
443406 select {
444407 case <- m .triggerSend :
445408 m .metrics .SentUpdates .Inc ()
446409 select {
447410 case m .syncCh <- m .allGroups ():
411+ lastSent = time .Now ()
448412 default :
449413 m .metrics .DelayedUpdates .Inc ()
450414 m .logger .Debug ("Discovery receiver's channel was full so will retry the next cycle" )
415+ // Ensure we don't miss this update.
451416 select {
452417 case m .triggerSend <- struct {}{}:
453418 default :
454419 }
420+
455421 }
456422 default :
457423 }
424+ if time .Since (lastSent ) > m .updatert {
425+ b .Reset () // Nothing happened for a while, start again from low interval for prompt updates.
426+ }
458427 }
459428 }
460429}
0 commit comments