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

     1  package v3
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"net/http"
     7  	"net/url"
     8  
     9  	"github.com/ansel1/merry"
    10  	protov3 "github.com/go-graphite/protocol/carbonapi_v3_pb"
    11  	"go.uber.org/zap"
    12  
    13  	"github.com/go-graphite/carbonapi/limiter"
    14  	"github.com/go-graphite/carbonapi/zipper/helper"
    15  	"github.com/go-graphite/carbonapi/zipper/httpHeaders"
    16  	"github.com/go-graphite/carbonapi/zipper/metadata"
    17  	"github.com/go-graphite/carbonapi/zipper/types"
    18  )
    19  
    20  const (
    21  	format = "carbonapi_v3_pb"
    22  )
    23  
    24  func init() {
    25  	aliases := []string{"carbonapi_v3_pb", "proto_v3_pb", "v3_pb"}
    26  	metadata.Metadata.Lock()
    27  	for _, name := range aliases {
    28  		metadata.Metadata.SupportedProtocols[name] = struct{}{}
    29  		metadata.Metadata.ProtocolInits[name] = New
    30  		metadata.Metadata.ProtocolInitsWithLimiter[name] = NewWithLimiter
    31  	}
    32  	defer metadata.Metadata.Unlock()
    33  }
    34  
    35  // RoundRobin is used to connect to backends inside clientGroups, implements BackendServer interface
    36  type ClientProtoV3Group struct {
    37  	groupName string
    38  	servers   []string
    39  
    40  	client *http.Client
    41  
    42  	limiter              limiter.ServerLimiter
    43  	logger               *zap.Logger
    44  	timeout              types.Timeouts
    45  	maxTries             int
    46  	maxMetricsPerRequest int
    47  
    48  	httpQuery *helper.HttpQuery
    49  }
    50  
    51  func (c *ClientProtoV3Group) Children() []types.BackendServer {
    52  	return []types.BackendServer{c}
    53  }
    54  
    55  func New(logger *zap.Logger, config types.BackendV2, tldCacheDisabled, requireSuccessAll bool) (types.BackendServer, merry.Error) {
    56  	if config.ConcurrencyLimit == nil {
    57  		return nil, types.ErrConcurrencyLimitNotSet
    58  	}
    59  	if len(config.Servers) == 0 {
    60  		return nil, types.ErrNoServersSpecified
    61  	}
    62  	l := limiter.NewServerLimiter(config.Servers, *config.ConcurrencyLimit)
    63  
    64  	return NewWithLimiter(logger, config, tldCacheDisabled, requireSuccessAll, l)
    65  }
    66  
    67  func NewWithLimiter(logger *zap.Logger, config types.BackendV2, tldCacheDisabled, requireSuccessAll bool, l limiter.ServerLimiter) (types.BackendServer, merry.Error) {
    68  	logger = logger.With(zap.String("type", "protoV3Group"), zap.String("name", config.GroupName))
    69  
    70  	httpClient := helper.GetHTTPClient(logger, config)
    71  
    72  	httpQuery := helper.NewHttpQuery(config.GroupName, config.Servers, *config.MaxTries, l, httpClient, httpHeaders.ContentTypeCarbonAPIv3PB)
    73  
    74  	c := &ClientProtoV3Group{
    75  		groupName:            config.GroupName,
    76  		servers:              config.Servers,
    77  		timeout:              *config.Timeouts,
    78  		maxTries:             *config.MaxTries,
    79  		maxMetricsPerRequest: *config.MaxBatchSize,
    80  
    81  		client:  httpClient,
    82  		limiter: l,
    83  		logger:  logger,
    84  
    85  		httpQuery: httpQuery,
    86  	}
    87  	return c, nil
    88  }
    89  
    90  func (c ClientProtoV3Group) MaxMetricsPerRequest() int {
    91  	return c.maxMetricsPerRequest
    92  }
    93  
    94  func (c ClientProtoV3Group) Name() string {
    95  	return c.groupName
    96  }
    97  
    98  func (c ClientProtoV3Group) Backends() []string {
    99  	return c.servers
   100  }
   101  
   102  func (c *ClientProtoV3Group) Fetch(ctx context.Context, request *protov3.MultiFetchRequest) (*protov3.MultiFetchResponse, *types.Stats, merry.Error) {
   103  	stats := &types.Stats{
   104  		RenderRequests: 1,
   105  	}
   106  	rewrite, _ := url.Parse("http://127.0.0.1/render/")
   107  	logger := c.logger.With(zap.String("type", "fetch"), zap.String("request", request.String()))
   108  
   109  	v := url.Values{
   110  		"format": []string{format},
   111  	}
   112  	rewrite.RawQuery = v.Encode()
   113  
   114  	res, err := c.httpQuery.DoQuery(ctx, logger, rewrite.RequestURI(), types.MultiFetchRequestV3{MultiFetchRequest: *request})
   115  	if err != nil {
   116  		stats.RenderErrors = 1
   117  		if merry.Is(err, types.ErrTimeoutExceeded) {
   118  			stats.Timeouts = 1
   119  			stats.RenderTimeouts = 1
   120  		}
   121  		logger.Warn("errors occurred while getting results",
   122  			zap.Any("errors", err),
   123  		)
   124  		return nil, stats, err
   125  	}
   126  
   127  	if res == nil {
   128  		return nil, stats, types.ErrNoResponseFetched
   129  	}
   130  
   131  	var r protov3.MultiFetchResponse
   132  	err2 := r.Unmarshal(res.Response)
   133  	if err2 != nil {
   134  		stats.FailedServers = []string{res.Server}
   135  		stats.RenderErrors++
   136  		logger.Warn("errors occurred while getting results",
   137  			zap.Any("errors", err2),
   138  		)
   139  		return nil, stats, merry.Wrap(err2)
   140  	}
   141  
   142  	return &r, stats, nil
   143  }
   144  
   145  func (c *ClientProtoV3Group) Find(ctx context.Context, request *protov3.MultiGlobRequest) (*protov3.MultiGlobResponse, *types.Stats, merry.Error) {
   146  	logger := c.logger.With(zap.String("type", "find"), zap.Strings("request", request.Metrics))
   147  	stats := &types.Stats{
   148  		FindRequests: 1,
   149  	}
   150  	rewrite, _ := url.Parse("http://127.0.0.1/metrics/find/")
   151  
   152  	v := url.Values{
   153  		"format": []string{format},
   154  	}
   155  	rewrite.RawQuery = v.Encode()
   156  
   157  	res, err := c.httpQuery.DoQuery(ctx, logger, rewrite.RequestURI(), types.MultiGlobRequestV3{MultiGlobRequest: *request})
   158  	if err != nil {
   159  		stats.FindErrors = 1
   160  		if merry.Is(err, types.ErrTimeoutExceeded) {
   161  			stats.Timeouts = 1
   162  			stats.FindTimeouts = 1
   163  		}
   164  		return nil, stats, err
   165  	}
   166  
   167  	if res == nil {
   168  		return nil, stats, types.ErrNotFound
   169  	}
   170  	var globs protov3.MultiGlobResponse
   171  	err2 := globs.Unmarshal(res.Response)
   172  	if err2 != nil {
   173  		stats.FailedServers = []string{res.Server}
   174  		stats.FindErrors = 1
   175  		return nil, nil, merry.Wrap(err2)
   176  	}
   177  
   178  	return &globs, stats, nil
   179  }
   180  
   181  func (c *ClientProtoV3Group) Info(ctx context.Context, request *protov3.MultiMetricsInfoRequest) (*protov3.ZipperInfoResponse, *types.Stats, merry.Error) {
   182  	logger := c.logger.With(zap.String("type", "info"), zap.String("request", request.String()))
   183  	stats := &types.Stats{
   184  		InfoRequests: 1,
   185  	}
   186  	rewrite, _ := url.Parse("http://127.0.0.1/metrics/find/")
   187  
   188  	v := url.Values{
   189  		"format": []string{format},
   190  	}
   191  	rewrite.RawQuery = v.Encode()
   192  
   193  	res, err := c.httpQuery.DoQuery(ctx, logger, rewrite.RequestURI(), types.MultiMetricsInfoV3{MultiMetricsInfoRequest: *request})
   194  	if err != nil {
   195  		stats.InfoErrors = 1
   196  		if merry.Is(err, types.ErrTimeoutExceeded) {
   197  			stats.Timeouts = 1
   198  			stats.InfoTimeouts = 1
   199  		}
   200  		return nil, stats, err
   201  	}
   202  
   203  	if res == nil {
   204  		return nil, stats, types.ErrNoResponseFetched
   205  	}
   206  	var infos protov3.MultiMetricsInfoResponse
   207  	err2 := infos.Unmarshal(res.Response)
   208  	if err2 != nil {
   209  		stats.FailedServers = []string{res.Server}
   210  		stats.InfoErrors = 1
   211  		return nil, nil, merry.Wrap(err2)
   212  	}
   213  
   214  	stats.MemoryUsage = int64(infos.Size())
   215  
   216  	r := &protov3.ZipperInfoResponse{
   217  		Info: map[string]protov3.MultiMetricsInfoResponse{
   218  			c.Name(): infos,
   219  		},
   220  	}
   221  
   222  	return r, stats, nil
   223  }
   224  
   225  func (c *ClientProtoV3Group) List(ctx context.Context) (*protov3.ListMetricsResponse, *types.Stats, merry.Error) {
   226  	return nil, nil, types.ErrNotImplementedYet
   227  }
   228  func (c *ClientProtoV3Group) Stats(ctx context.Context) (*protov3.MetricDetailsResponse, *types.Stats, merry.Error) {
   229  	return nil, nil, types.ErrNotImplementedYet
   230  }
   231  
   232  func (c *ClientProtoV3Group) doTagQuery(ctx context.Context, isTagName bool, query string, limit int64) ([]string, merry.Error) {
   233  	logger := c.logger
   234  	var rewrite *url.URL
   235  	if isTagName {
   236  		logger = logger.With(zap.String("sub_type", "tagName"))
   237  		rewrite, _ = url.Parse("http://127.0.0.1/tags/autoComplete/tags")
   238  	} else {
   239  		logger = logger.With(zap.String("sub_type", "tagValues"))
   240  		rewrite, _ = url.Parse("http://127.0.0.1/tags/autoComplete/values")
   241  	}
   242  
   243  	var r []string
   244  
   245  	rewrite.RawQuery = query
   246  	res, e := c.httpQuery.DoQuery(ctx, logger, rewrite.RequestURI(), nil)
   247  	if e != nil {
   248  		return r, e
   249  	}
   250  
   251  	err := json.Unmarshal(res.Response, &r)
   252  	if err != nil {
   253  		return r, merry.Wrap(err)
   254  	}
   255  
   256  	logger.Debug("got client response",
   257  		zap.Any("r", r),
   258  	)
   259  
   260  	return r, nil
   261  }
   262  
   263  func (c *ClientProtoV3Group) TagNames(ctx context.Context, query string, limit int64) ([]string, merry.Error) {
   264  	return c.doTagQuery(ctx, true, query, limit)
   265  }
   266  
   267  func (c *ClientProtoV3Group) TagValues(ctx context.Context, query string, limit int64) ([]string, merry.Error) {
   268  	return c.doTagQuery(ctx, false, query, limit)
   269  }
   270  
   271  func (c *ClientProtoV3Group) ProbeTLDs(ctx context.Context) ([]string, merry.Error) {
   272  	logger := c.logger.With(zap.String("function", "prober"))
   273  	req := &protov3.MultiGlobRequest{
   274  		Metrics: []string{"*"},
   275  	}
   276  
   277  	logger.Debug("doing request",
   278  		zap.Strings("request", req.Metrics),
   279  	)
   280  
   281  	res, _, err := c.Find(ctx, req)
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  
   286  	if res == nil {
   287  		return nil, err
   288  	}
   289  	var tlds []string
   290  	for _, m := range res.Metrics {
   291  		for _, v := range m.Matches {
   292  			tlds = append(tlds, v.Path)
   293  		}
   294  	}
   295  
   296  	logger.Debug("will return data",
   297  		zap.Strings("tlds", tlds),
   298  	)
   299  
   300  	return tlds, nil
   301  }