github.com/go-graphite/carbonapi@v0.17.0/zipper/protocols/victoriametrics/victoriametrics_group.go (about) 1 package victoriametrics 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "strconv" 8 "sync/atomic" 9 "time" 10 11 "github.com/ansel1/merry" 12 "github.com/valyala/fastjson" 13 14 "github.com/go-graphite/carbonapi/limiter" 15 "github.com/go-graphite/carbonapi/zipper/helper" 16 "github.com/go-graphite/carbonapi/zipper/httpHeaders" 17 "github.com/go-graphite/carbonapi/zipper/metadata" 18 "github.com/go-graphite/carbonapi/zipper/types" 19 20 "github.com/go-graphite/carbonapi/zipper/protocols/prometheus" 21 22 "go.uber.org/zap" 23 ) 24 25 func init() { 26 aliases := []string{"victoriametrics", "vm"} 27 metadata.Metadata.Lock() 28 for _, name := range aliases { 29 metadata.Metadata.SupportedProtocols[name] = struct{}{} 30 metadata.Metadata.ProtocolInits[name] = New 31 metadata.Metadata.ProtocolInitsWithLimiter[name] = NewWithLimiter 32 } 33 defer metadata.Metadata.Unlock() 34 } 35 36 // VictoriaMetricsGroup is a protocol group that can query victoria-metrics 37 type VictoriaMetricsGroup struct { 38 types.BackendServer 39 40 groupName string 41 servers []string 42 protocol string 43 44 client *http.Client 45 46 limiter limiter.ServerLimiter 47 logger *zap.Logger 48 timeout types.Timeouts 49 maxTries int 50 maxMetricsPerRequest int 51 52 step int64 53 maxPointsPerQuery int64 54 forceMinStepInterval time.Duration 55 vmClusterTenantID string 56 57 startDelay prometheus.StartDelay 58 probeVersionInterval time.Duration 59 fallbackVersion string 60 61 httpQuery *helper.HttpQuery 62 parserPool fastjson.ParserPool 63 64 featureSet atomic.Value // *vmSupportedFeatures 65 } 66 67 func NewWithLimiter(logger *zap.Logger, config types.BackendV2, tldCacheDisabled, requireSuccessAll bool, limiter limiter.ServerLimiter) (types.BackendServer, merry.Error) { 68 logger = logger.With(zap.String("type", "victoriametrics"), zap.String("protocol", config.Protocol), zap.String("name", config.GroupName)) 69 httpClient := helper.GetHTTPClient(logger, config) 70 71 step := int64(15) 72 var vmClusterTenantID string = "" 73 vmClusterTenantIDI, ok := config.BackendOptions["vmclustertenantid"] 74 if ok { 75 vmClusterTenantID = vmClusterTenantIDI.(string) 76 } 77 stepI, ok := config.BackendOptions["step"] 78 if ok { 79 stepNew, ok := stepI.(string) 80 if ok { 81 if stepNew[len(stepNew)-1] >= '0' && stepNew[len(stepNew)-1] <= '9' { 82 stepNew += "s" 83 } 84 t, err := time.ParseDuration(stepNew) 85 if err != nil { 86 logger.Fatal("failed to parse option", 87 zap.String("option_name", "step"), 88 zap.String("option_value", stepNew), 89 zap.Error(err), 90 ) 91 } 92 step = int64(t.Seconds()) 93 } else { 94 logger.Fatal("failed to parse step", 95 zap.String("type_parsed", fmt.Sprintf("%T", stepI)), 96 zap.String("type_expected", "string"), 97 ) 98 } 99 } 100 101 maxPointsPerQuery := int64(11000) 102 mppqI, ok := config.BackendOptions["max_points_per_query"] 103 if ok { 104 mppq, ok := mppqI.(int) 105 if !ok { 106 logger.Fatal("failed to parse max_points_per_query", 107 zap.String("type_parsed", fmt.Sprintf("%T", mppqI)), 108 zap.String("type_expected", "int"), 109 ) 110 } 111 112 maxPointsPerQuery = int64(mppq) 113 } 114 115 var forceMinStepInterval time.Duration 116 fmsiI, ok := config.BackendOptions["force_min_step_interval"] 117 if ok { 118 fmsiS, ok := fmsiI.(string) 119 if !ok { 120 logger.Fatal("failed to parse force_min_step_interval", 121 zap.String("type_parsed", fmt.Sprintf("%T", fmsiI)), 122 zap.String("type_expected", "time.Duration"), 123 ) 124 } 125 var err error 126 forceMinStepInterval, err = time.ParseDuration(fmsiS) 127 if err != nil { 128 logger.Fatal("failed to parse force_min_step_interval", 129 zap.String("value_provided", fmsiS), 130 zap.String("type_expected", "time.Duration"), 131 ) 132 } 133 } 134 135 delay := prometheus.StartDelay{ 136 IsSet: false, 137 IsDuration: false, 138 T: -1, 139 } 140 startI, ok := config.BackendOptions["start"] 141 if ok { 142 delay.IsSet = true 143 startNew, ok := startI.(string) 144 if ok { 145 startNewInt, err := strconv.Atoi(startNew) 146 if err != nil { 147 d, err2 := time.ParseDuration(startNew) 148 if err2 != nil { 149 logger.Fatal("failed to parse option", 150 zap.String("option_name", "start"), 151 zap.String("option_value", startNew), 152 zap.Errors("errors", []error{err, err2}), 153 ) 154 } 155 delay.IsDuration = true 156 delay.D = d 157 } else { 158 delay.T = int64(startNewInt) 159 } 160 } 161 } 162 163 periodicProbe := false 164 probeVersionInterval, _ := time.ParseDuration("600s") 165 probeVersionIntervalParam, ok := config.BackendOptions["probe_version_interval"] 166 if ok { 167 probeVersionIntervalStr, ok := probeVersionIntervalParam.(string) 168 if ok && probeVersionIntervalStr != "never" { 169 interval, err := time.ParseDuration(probeVersionIntervalStr) 170 if err != nil { 171 logger.Fatal("failed to parse option", 172 zap.String("option_name", "start"), 173 zap.String("option_value", probeVersionIntervalStr), 174 zap.Errors("errors", []error{err}), 175 ) 176 } 177 probeVersionInterval = interval 178 periodicProbe = true 179 } else { 180 logger.Fatal("failed to parse option", 181 zap.String("option_name", "start"), 182 zap.Any("option_value", probeVersionIntervalParam), 183 zap.Errors("errors", []error{fmt.Errorf("not a string")}), 184 ) 185 } 186 } else { 187 periodicProbe = true 188 } 189 190 fallbackVersion := "v0.0.0" 191 fallbackVersionParam, ok := config.BackendOptions["fallback_version"] 192 if ok { 193 fallbackVersion, ok = fallbackVersionParam.(string) 194 if !ok { 195 logger.Fatal("failed to parse option", 196 zap.String("option_name", "start"), 197 zap.Any("option_value", probeVersionIntervalParam), 198 zap.Errors("errors", []error{fmt.Errorf("not a string")}), 199 ) 200 } 201 } 202 203 httpQuery := helper.NewHttpQuery(config.GroupName, config.Servers, *config.MaxTries, limiter, httpClient, httpHeaders.ContentTypeCarbonAPIv2PB) 204 205 c := &VictoriaMetricsGroup{ 206 groupName: config.GroupName, 207 servers: config.Servers, 208 protocol: config.Protocol, 209 timeout: *config.Timeouts, 210 maxTries: *config.MaxTries, 211 maxMetricsPerRequest: *config.MaxBatchSize, 212 213 step: step, 214 maxPointsPerQuery: maxPointsPerQuery, 215 vmClusterTenantID: vmClusterTenantID, 216 startDelay: delay, 217 probeVersionInterval: probeVersionInterval, 218 fallbackVersion: fallbackVersion, 219 220 client: httpClient, 221 limiter: limiter, 222 logger: logger, 223 224 httpQuery: httpQuery, 225 } 226 227 promLogger := logger.With(zap.String("subclass", "prometheus")) 228 c.BackendServer, _ = prometheus.NewWithEverythingInitialized(promLogger, config, tldCacheDisabled, requireSuccessAll, limiter, step, maxPointsPerQuery, forceMinStepInterval, delay, httpQuery, httpClient) 229 230 c.updateFeatureSet(context.Background()) 231 232 logger.Info("periodic probe for version change", 233 zap.Bool("enabled", periodicProbe), 234 zap.Duration("interval", c.probeVersionInterval), 235 ) 236 if periodicProbe { 237 go c.probeVMVersion(context.Background()) 238 } 239 240 return c, nil 241 } 242 243 func New(logger *zap.Logger, config types.BackendV2, tldCacheDisabled, requireSuccessAll bool) (types.BackendServer, merry.Error) { 244 if config.ConcurrencyLimit == nil { 245 return nil, types.ErrConcurrencyLimitNotSet 246 } 247 if len(config.Servers) == 0 { 248 return nil, types.ErrNoServersSpecified 249 } 250 l := limiter.NewServerLimiter([]string{config.GroupName}, *config.ConcurrencyLimit) 251 252 return NewWithLimiter(logger, config, tldCacheDisabled, requireSuccessAll, l) 253 }