github.com/grafana/pyroscope@v1.18.0/pkg/segmentwriter/client/distributor/placement/adaptiveplacement/placement_agent.go (about) 1 package adaptiveplacement 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "time" 8 9 "github.com/go-kit/log" 10 "github.com/go-kit/log/level" 11 "github.com/grafana/dskit/services" 12 "github.com/prometheus/client_golang/prometheus" 13 14 "github.com/grafana/pyroscope/pkg/segmentwriter/client/distributor/placement/adaptiveplacement/adaptive_placementpb" 15 "github.com/grafana/pyroscope/pkg/util" 16 ) 17 18 type Agent struct { 19 service services.Service 20 logger log.Logger 21 store StoreReader 22 config Config 23 metrics *agentMetrics 24 25 placement *AdaptivePlacement 26 rules *adaptive_placementpb.PlacementRules 27 } 28 29 func NewAgent( 30 logger log.Logger, 31 reg prometheus.Registerer, 32 config Config, 33 limits Limits, 34 store Store, 35 ) *Agent { 36 a := Agent{ 37 logger: logger, 38 store: store, 39 config: config, 40 placement: NewAdaptivePlacement(limits), 41 metrics: newAgentMetrics(reg), 42 } 43 a.service = services.NewTimerService( 44 config.PlacementUpdateInterval, 45 a.starting, 46 a.loadRulesNoError, 47 a.stopping, 48 ) 49 return &a 50 } 51 52 func (a *Agent) Service() services.Service { return a.service } 53 54 func (a *Agent) Placement() *AdaptivePlacement { return a.placement } 55 56 func (a *Agent) starting(ctx context.Context) error { 57 _ = a.loadRulesNoError(ctx) 58 if a.rules == nil { 59 // The exact reason is logged in loadRules. 60 return fmt.Errorf("failed to load placement rules") 61 } 62 return nil 63 } 64 65 func (a *Agent) stopping(error) error { return nil } 66 67 // The function is only needed to satisfy the services.OneIteration 68 // signature: there's no case when the service stops on its own: 69 // it's better to serve outdated rules than to not serve at all. 70 func (a *Agent) loadRulesNoError(ctx context.Context) error { 71 util.Recover(func() { a.loadRules(ctx) }) 72 return nil 73 } 74 75 func (a *Agent) loadRules(ctx context.Context) { 76 rules, err := a.store.LoadRules(ctx) 77 switch { 78 case err == nil: 79 case errors.Is(err, ErrRulesNotFound): 80 _ = level.Warn(a.logger).Log("msg", "placement rules not found") 81 rules = &adaptive_placementpb.PlacementRules{CreatedAt: time.Now().UnixNano()} 82 default: 83 _ = level.Error(a.logger).Log("msg", "failed to load placement rules", "err", err) 84 return 85 } 86 a.metrics.lag.Set(max(0, time.Since(time.Unix(0, rules.CreatedAt)).Seconds())) 87 if a.rules != nil { 88 if rules.CreatedAt < a.rules.CreatedAt { 89 _ = level.Warn(a.logger).Log( 90 "msg", "placement rules are outdated", 91 "discovered", time.Unix(0, rules.CreatedAt), 92 "loaded", time.Unix(0, a.rules.CreatedAt), 93 ) 94 return 95 } 96 } 97 _ = level.Debug(a.logger).Log( 98 "msg", "loading placement rules", 99 "created_at", time.Unix(0, rules.CreatedAt), 100 ) 101 a.placement.Update(rules) 102 a.rules = rules 103 }