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  }