github.com/go-graphite/carbonapi@v0.17.0/zipper/protocols/victoriametrics/feature_set.go (about)

     1  package victoriametrics
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"net/url"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"go.uber.org/zap"
    13  )
    14  
    15  type vmSupportedFeatures struct {
    16  	vmVersion                     string
    17  	versionParsed                 [3]int64
    18  	SupportGraphiteFindAPI        bool
    19  	SupportGraphiteTagsAPI        bool
    20  	GraphiteTagsAPIRequiresDedupe bool
    21  	SupportOptimizedGraphiteFetch bool
    22  }
    23  
    24  // Example: v1.46.0
    25  func versionToFeatureSet(logger *zap.Logger, version string) *vmSupportedFeatures {
    26  	logger = logger.With(zap.String("function", "versionToFeatureSet"))
    27  	res := &vmSupportedFeatures{
    28  		vmVersion: version,
    29  	}
    30  
    31  	verSplitted := strings.Split(version, ".")
    32  	if len(verSplitted) < 3 {
    33  		logger.Warn("failed to parse version string",
    34  			zap.Strings("version_array", verSplitted),
    35  			zap.Error(fmt.Errorf("expected at least 3 components")),
    36  		)
    37  		return res
    38  	}
    39  	if verSplitted[0] != "v1" {
    40  		return res
    41  	}
    42  	res.versionParsed[0] = 1
    43  
    44  	v2, err := strconv.ParseInt(verSplitted[1], 10, 64)
    45  	if err != nil {
    46  		logger.Warn("failed to parse version string",
    47  			zap.Strings("version_array", verSplitted),
    48  			zap.Error(err),
    49  		)
    50  		return res
    51  	}
    52  	res.versionParsed[1] = v2
    53  
    54  	ver3Raw := strings.Split(verSplitted[2], "-")[0]
    55  	v3, err := strconv.ParseInt(ver3Raw, 10, 64)
    56  	if err != nil {
    57  		logger.Warn("failed to parse version string",
    58  			zap.Strings("version_array", verSplitted),
    59  			zap.Error(err),
    60  		)
    61  		return res
    62  	}
    63  	res.versionParsed[2] = v3
    64  
    65  	if v2 >= 42 {
    66  		res.SupportGraphiteFindAPI = true
    67  	}
    68  
    69  	if v2 >= 47 {
    70  		res.SupportGraphiteTagsAPI = true
    71  		res.GraphiteTagsAPIRequiresDedupe = true
    72  	}
    73  
    74  	if v2 >= 50 {
    75  		res.GraphiteTagsAPIRequiresDedupe = false
    76  	}
    77  
    78  	if v2 > 53 || (v2 == 53 && v3 >= 1) {
    79  		res.SupportOptimizedGraphiteFetch = true
    80  	}
    81  
    82  	return res
    83  }
    84  
    85  func parseVMVersion(in []byte, fallbackVersion string) string {
    86  	/*
    87  		Logic:
    88  		Step1:
    89  			Input:
    90  				process_start_time_seconds 1607269249
    91  				process_virtual_memory_bytes 3207864320
    92  				vm_app_version{version="victoria-metrics-20201109-230848-tags-v1.46.0-0-g41813eb8", short_version="v1.46.0"} 1
    93  				vm_allowed_memory_bytes 20197038489
    94  				vm_app_start_timestamp 1607269249
    95  
    96  			Output:
    97  				{version="victoria-metrics-20201109-230848-tags-v1.46.0-0-g41813eb8", short_version="v1.46.0"} 1
    98  				vm_allowed_memory_bytes 20197038489
    99  				vm_app_start_timestamp 1607269249
   100  
   101  		Step2:
   102  			Input:
   103  				{version="victoria-metrics-20201109-230848-tags-v1.46.0-0-g41813eb8", short_version="v1.46.0"} 1
   104  				vm_allowed_memory_bytes 20197038489
   105  				vm_app_start_timestamp 1607269249
   106  
   107  			Output:
   108  				v1.46.0"} 1
   109  				vm_allowed_memory_bytes 20197038489
   110  				vm_app_start_timestamp 1607269249
   111  		Step3:
   112  			Input:
   113  				v1.46.0"} 1
   114  				vm_allowed_memory_bytes 20197038489
   115  				vm_app_start_timestamp 1607269249
   116  
   117  			Output:
   118  				v1.46.0
   119  	*/
   120  	tokens := [][]byte{
   121  		[]byte("vm_app_version"),
   122  		[]byte("short_version=\""),
   123  		[]byte("\""),
   124  	}
   125  
   126  	idx := 0
   127  	for i := range tokens {
   128  		l := 0
   129  		if i != 0 {
   130  			l = len(tokens[i-1])
   131  		}
   132  		in = in[idx+l:]
   133  		idx = bytes.Index(in, tokens[i])
   134  		if idx == -1 {
   135  			return fallbackVersion
   136  		}
   137  	}
   138  
   139  	in = in[:idx]
   140  	if len(in) == 0 {
   141  		return fallbackVersion
   142  	}
   143  
   144  	return string(in)
   145  }
   146  
   147  func (c *VictoriaMetricsGroup) updateFeatureSet(ctx context.Context) {
   148  	logger := c.logger.With(zap.String("function", "updateFeatureSet"))
   149  	rewrite, _ := url.Parse("http://127.0.0.1/metrics")
   150  	var minFeatureSet *vmSupportedFeatures
   151  
   152  	res, queryErr := c.httpQuery.DoQueryToAll(ctx, logger, rewrite.RequestURI(), nil)
   153  	if queryErr != nil {
   154  		logger.Debug("got some errors while getting capabilities",
   155  			zap.Error(queryErr),
   156  		)
   157  	}
   158  	if len(res) == 0 {
   159  		return
   160  	}
   161  
   162  	for i := range res {
   163  		if res[i] == nil || res[i].Response == nil {
   164  			continue
   165  		}
   166  		version := parseVMVersion(res[i].Response, c.fallbackVersion)
   167  		featureSet := versionToFeatureSet(logger, version)
   168  		if minFeatureSet == nil {
   169  			minFeatureSet = featureSet
   170  			continue
   171  		}
   172  
   173  		if minFeatureSet.versionParsed[0] > featureSet.versionParsed[0] ||
   174  			minFeatureSet.versionParsed[1] > featureSet.versionParsed[1] ||
   175  			minFeatureSet.versionParsed[2] > featureSet.versionParsed[2] {
   176  			minFeatureSet = featureSet
   177  		}
   178  	}
   179  
   180  	logger.Debug("got feature set",
   181  		zap.Any("featureset", minFeatureSet),
   182  	)
   183  
   184  	if minFeatureSet == nil {
   185  		minFeatureSet = versionToFeatureSet(logger, "c.fallbackVersion")
   186  	}
   187  
   188  	c.featureSet.Store(minFeatureSet)
   189  }
   190  
   191  func (c *VictoriaMetricsGroup) probeVMVersion(ctx context.Context) {
   192  	ticker := time.NewTicker(c.probeVersionInterval)
   193  
   194  	for {
   195  		select {
   196  		case <-ctx.Done():
   197  			return
   198  		case <-ticker.C:
   199  			c.updateFeatureSet(ctx)
   200  		}
   201  	}
   202  }