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 }