Path: blob/main/pkg/metrics/instance/modal_manager.go
4094 views
package instance12import (3"fmt"4"sync"56"github.com/go-kit/log"7"github.com/go-kit/log/level"8"github.com/prometheus/client_golang/prometheus"9"github.com/prometheus/client_golang/prometheus/promauto"10)1112// Mode controls how instances are created.13type Mode string1415// Types of instance modes16var (17ModeDistinct Mode = "distinct"18ModeShared Mode = "shared"1920DefaultMode = ModeShared21)2223// UnmarshalYAML unmarshals a string to a Mode. Fails if the string is24// unrecognized.25func (m *Mode) UnmarshalYAML(unmarshal func(interface{}) error) error {26*m = DefaultMode2728var plain string29if err := unmarshal(&plain); err != nil {30return err31}3233switch plain {34case string(ModeDistinct):35*m = ModeDistinct36return nil37case string(ModeShared):38*m = ModeShared39return nil40default:41return fmt.Errorf("unsupported instance_mode '%s'. supported values 'shared', 'distinct'", plain)42}43}4445// ModalManager runs instances by either grouping them or running them fully46// separately.47type ModalManager struct {48mut sync.RWMutex49mode Mode50configs map[string]Config5152changedConfigs *prometheus.CounterVec53currentActiveConfigs prometheus.Gauge5455log log.Logger5657// The ModalManager wraps around a "final" Manager that is intended to58// launch and manage instances based on Configs. This is specified here by the59// "wrapped" Manager.60//61// However, there may be another manager performing formations on the configs62// before they are passed through to wrapped. This is specified by the "active"63// Manager.64//65// If no transformations on Configs are needed, active will be identical to66// wrapped.67wrapped, active Manager68}6970// NewModalManager creates a new ModalManager.71func NewModalManager(reg prometheus.Registerer, l log.Logger, next Manager, mode Mode) (*ModalManager, error) {72changedConfigs := promauto.With(reg).NewCounterVec(prometheus.CounterOpts{73Name: "agent_metrics_configs_changed_total",74Help: "Total number of dynamically updated configs",75}, []string{"event"})76currentActiveConfigs := promauto.With(reg).NewGauge(prometheus.GaugeOpts{77Name: "agent_metrics_active_configs",78Help: "Current number of active configs being used by the agent.",79})8081mm := ModalManager{82wrapped: next,83log: l,84changedConfigs: changedConfigs,85currentActiveConfigs: currentActiveConfigs,86configs: make(map[string]Config),87}88if err := mm.SetMode(mode); err != nil {89return nil, err90}91return &mm, nil92}9394// SetMode updates the mode ModalManager is running in. Changing the mode is95// an expensive operation; all underlying configs must be stopped and then96// reapplied.97func (m *ModalManager) SetMode(newMode Mode) error {98if newMode == "" {99newMode = DefaultMode100}101102m.mut.Lock()103defer m.mut.Unlock()104105var (106prevMode = m.mode107prevActive = m.active108)109110if prevMode == newMode {111return nil112}113114// Set the active Manager based on the new mode. "distinct" means no transformations115// need to be applied and we can use the wrapped Manager directly. Otherwise, we need116// to create a new Manager to apply transformations.117switch newMode {118case ModeDistinct:119m.active = m.wrapped120case ModeShared:121m.active = NewGroupManager(m.wrapped)122default:123panic("unknown mode " + m.mode)124}125m.mode = newMode126127// Remove all configs from the previous active Manager.128if prevActive != nil {129prevActive.Stop()130}131132// Re-apply configs to the new active Manager.133var firstError error134for name, cfg := range m.configs {135err := m.active.ApplyConfig(cfg)136if err != nil {137level.Error(m.log).Log("msg", "failed to apply config when changing modes", "name", name, "prev_mode", prevMode, "new_mode", newMode, "err", err)138}139if firstError == nil && err != nil {140firstError = err141}142}143144return firstError145}146147// GetInstance implements Manager.148func (m *ModalManager) GetInstance(name string) (ManagedInstance, error) {149m.mut.RLock()150defer m.mut.RUnlock()151return m.active.GetInstance(name)152}153154// ListInstances implements Manager.155func (m *ModalManager) ListInstances() map[string]ManagedInstance {156m.mut.RLock()157defer m.mut.RUnlock()158return m.active.ListInstances()159}160161// ListConfigs implements Manager.162func (m *ModalManager) ListConfigs() map[string]Config {163m.mut.RLock()164defer m.mut.RUnlock()165return m.active.ListConfigs()166}167168// ApplyConfig implements Manager.169func (m *ModalManager) ApplyConfig(c Config) error {170m.mut.Lock()171defer m.mut.Unlock()172173if err := m.active.ApplyConfig(c); err != nil {174return err175}176177if _, existingConfig := m.configs[c.Name]; !existingConfig {178m.currentActiveConfigs.Inc()179m.changedConfigs.WithLabelValues("created").Inc()180} else {181m.changedConfigs.WithLabelValues("updated").Inc()182}183184m.configs[c.Name] = c185186return nil187}188189// DeleteConfig implements Manager.190func (m *ModalManager) DeleteConfig(name string) error {191m.mut.Lock()192defer m.mut.Unlock()193194if err := m.active.DeleteConfig(name); err != nil {195return err196}197198if _, existingConfig := m.configs[name]; existingConfig {199m.currentActiveConfigs.Dec()200delete(m.configs, name)201}202203m.changedConfigs.WithLabelValues("deleted").Inc()204return nil205}206207// Stop implements Manager.208func (m *ModalManager) Stop() {209m.mut.Lock()210defer m.mut.Unlock()211212m.active.Stop()213m.currentActiveConfigs.Set(0)214m.configs = make(map[string]Config)215}216217218