github.com/m3db/m3@v1.5.0/src/query/remote/codecs.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package remote 22 23 import ( 24 "context" 25 "fmt" 26 "net/http" 27 "strings" 28 "time" 29 30 "github.com/m3db/m3/src/metrics/policy" 31 "github.com/m3db/m3/src/query/api/v1/handler/prometheus/handleroptions" 32 "github.com/m3db/m3/src/query/block" 33 "github.com/m3db/m3/src/query/generated/proto/rpcpb" 34 rpc "github.com/m3db/m3/src/query/generated/proto/rpcpb" 35 "github.com/m3db/m3/src/query/models" 36 "github.com/m3db/m3/src/query/storage" 37 "github.com/m3db/m3/src/query/storage/m3/storagemetadata" 38 "github.com/m3db/m3/src/query/util/logging" 39 "github.com/m3db/m3/src/x/instrument" 40 xtime "github.com/m3db/m3/src/x/time" 41 42 "google.golang.org/grpc/metadata" 43 ) 44 45 const reqIDKey = "reqid" 46 47 func fromTime(t time.Time) int64 { 48 return storage.TimeToPromTimestamp(xtime.ToUnixNano(t)) 49 } 50 51 func toTime(t int64) time.Time { 52 return storage.PromTimestampToTime(t) 53 } 54 55 func encodeTags(tags models.Tags) []*rpc.Tag { 56 encodedTags := make([]*rpc.Tag, 0, tags.Len()) 57 for _, t := range tags.Tags { 58 encodedTags = append(encodedTags, &rpc.Tag{ 59 Name: t.Name, 60 Value: t.Value, 61 }) 62 } 63 64 return encodedTags 65 } 66 67 // encodeFetchResult encodes fetch result to rpc response 68 func encodeFetchResult(results *storage.FetchResult) *rpc.FetchResponse { 69 series := make([]*rpc.Series, len(results.SeriesList)) 70 for i, result := range results.SeriesList { 71 vLen := result.Len() 72 datapoints := make([]*rpc.Datapoint, vLen) 73 for j := 0; j < vLen; j++ { 74 dp := result.Values().DatapointAt(j) 75 datapoints[j] = &rpc.Datapoint{ 76 Timestamp: int64(dp.Timestamp), 77 Value: dp.Value, 78 } 79 } 80 81 series[i] = &rpc.Series{ 82 Meta: &rpc.SeriesMetadata{ 83 Id: result.Name(), 84 }, 85 Value: &rpc.Series_Decompressed{ 86 Decompressed: &rpc.DecompressedSeries{ 87 Datapoints: datapoints, 88 Tags: encodeTags(result.Tags), 89 }, 90 }, 91 } 92 } 93 94 return &rpc.FetchResponse{ 95 Series: series, 96 Meta: encodeResultMetadata(results.Metadata), 97 } 98 } 99 100 // encodeFetchRequest encodes fetch request into rpc FetchRequest 101 func encodeFetchRequest( 102 query *storage.FetchQuery, 103 options *storage.FetchOptions, 104 ) (*rpc.FetchRequest, error) { 105 matchers, err := encodeTagMatchers(query.TagMatchers) 106 if err != nil { 107 return nil, err 108 } 109 110 opts, err := encodeFetchOptions(options) 111 if err != nil { 112 return nil, err 113 } 114 115 return &rpc.FetchRequest{ 116 Start: fromTime(query.Start), 117 End: fromTime(query.End), 118 Matchers: &rpc.FetchRequest_TagMatchers{ 119 TagMatchers: matchers, 120 }, 121 Options: opts, 122 }, nil 123 } 124 125 func encodeTagMatchers(modelMatchers models.Matchers) (*rpc.TagMatchers, error) { 126 if modelMatchers == nil { 127 return nil, nil 128 } 129 130 matchers := make([]*rpc.TagMatcher, len(modelMatchers)) 131 for i, matcher := range modelMatchers { 132 t, err := encodeMatcherTypeToProto(matcher.Type) 133 if err != nil { 134 return nil, err 135 } 136 137 matchers[i] = &rpc.TagMatcher{ 138 Name: matcher.Name, 139 Value: matcher.Value, 140 Type: t, 141 } 142 } 143 144 return &rpc.TagMatchers{ 145 TagMatchers: matchers, 146 }, nil 147 } 148 149 func encodeFanoutOption(opt storage.FanoutOption) (rpc.FanoutOption, error) { 150 switch opt { 151 case storage.FanoutDefault: 152 return rpc.FanoutOption_DEFAULT_OPTION, nil 153 case storage.FanoutForceDisable: 154 return rpc.FanoutOption_FORCE_DISABLED, nil 155 case storage.FanoutForceEnable: 156 return rpc.FanoutOption_FORCE_ENABLED, nil 157 } 158 159 return 0, fmt.Errorf("unknown fanout option for proto encoding: %v", opt) 160 } 161 162 func encodeFetchOptions(options *storage.FetchOptions) (*rpc.FetchOptions, error) { 163 if options == nil { 164 return nil, nil 165 } 166 167 fanoutOpts := options.FanoutOptions 168 result := &rpc.FetchOptions{ 169 Limit: int64(options.SeriesLimit), 170 Source: options.Source, 171 } 172 173 unagg, err := encodeFanoutOption(fanoutOpts.FanoutUnaggregated) 174 if err != nil { 175 return nil, err 176 } 177 178 result.Unaggregated = unagg 179 agg, err := encodeFanoutOption(fanoutOpts.FanoutAggregated) 180 if err != nil { 181 return nil, err 182 } 183 184 result.Aggregated = agg 185 aggOpt, err := encodeFanoutOption(fanoutOpts.FanoutAggregatedOptimized) 186 if err != nil { 187 return nil, err 188 } 189 190 result.AggregatedOptimized = aggOpt 191 if v := options.RestrictQueryOptions; v != nil { 192 restrict, err := encodeRestrictQueryOptions(v) 193 if err != nil { 194 return nil, err 195 } 196 197 result.Restrict = restrict 198 } 199 200 if v := options.LookbackDuration; v != nil { 201 result.LookbackDuration = int64(*v) 202 } 203 204 return result, nil 205 } 206 207 func encodeRestrictQueryOptionsByType( 208 o *storage.RestrictByType, 209 ) (*rpcpb.RestrictQueryType, error) { 210 if o == nil { 211 return nil, nil 212 } 213 214 if err := o.Validate(); err != nil { 215 return nil, err 216 } 217 218 result := &rpcpb.RestrictQueryType{} 219 switch o.MetricsType { 220 case storagemetadata.UnaggregatedMetricsType: 221 result.MetricsType = rpcpb.MetricsType_UNAGGREGATED_METRICS_TYPE 222 case storagemetadata.AggregatedMetricsType: 223 result.MetricsType = rpcpb.MetricsType_AGGREGATED_METRICS_TYPE 224 225 storagePolicyProto, err := o.StoragePolicy.Proto() 226 if err != nil { 227 return nil, err 228 } 229 230 result.MetricsStoragePolicy = storagePolicyProto 231 } 232 233 return result, nil 234 } 235 236 func encodeRestrictQueryOptionsByTag( 237 o *storage.RestrictByTag, 238 ) (*rpcpb.RestrictQueryTags, error) { 239 if o == nil { 240 return nil, nil 241 } 242 243 matchers, err := encodeTagMatchers(o.GetMatchers()) 244 if err != nil { 245 return nil, err 246 } 247 248 return &rpcpb.RestrictQueryTags{ 249 Restrict: matchers, 250 Strip: o.Strip, 251 }, nil 252 } 253 254 func encodeRestrictQueryOptions( 255 o *storage.RestrictQueryOptions, 256 ) (*rpcpb.RestrictQueryOptions, error) { 257 if o == nil { 258 return nil, nil 259 } 260 261 byType, err := encodeRestrictQueryOptionsByType(o.GetRestrictByType()) 262 if err != nil { 263 return nil, err 264 } 265 266 byTags, err := encodeRestrictQueryOptionsByTag(o.GetRestrictByTag()) 267 if err != nil { 268 return nil, err 269 } 270 271 return &rpcpb.RestrictQueryOptions{ 272 RestrictQueryType: byType, 273 RestrictQueryTags: byTags, 274 }, nil 275 } 276 277 func encodeMatcherTypeToProto(t models.MatchType) (rpc.MatcherType, error) { 278 switch t { 279 case models.MatchEqual: 280 return rpc.MatcherType_EQUAL, nil 281 case models.MatchNotEqual: 282 return rpc.MatcherType_NOTEQUAL, nil 283 case models.MatchRegexp: 284 return rpc.MatcherType_REGEXP, nil 285 case models.MatchNotRegexp: 286 return rpc.MatcherType_NOTREGEXP, nil 287 case models.MatchField: 288 return rpc.MatcherType_EXISTS, nil 289 case models.MatchNotField: 290 return rpc.MatcherType_NOTEXISTS, nil 291 case models.MatchAll: 292 return rpc.MatcherType_ALL, nil 293 default: 294 return 0, fmt.Errorf("unknown matcher type for proto encoding") 295 } 296 } 297 298 // encodeMetadata creates a context that propagates request metadata as well as requestID 299 func encodeMetadata(ctx context.Context, requestID string) context.Context { 300 if ctx == nil { 301 return ctx 302 } 303 304 headerValues := ctx.Value(handleroptions.RequestHeaderKey) 305 headers, ok := headerValues.(http.Header) 306 if !ok { 307 return metadata.NewOutgoingContext(ctx, metadata.MD{reqIDKey: []string{requestID}}) 308 } 309 310 return metadata.NewOutgoingContext(ctx, convertHeaderToMetaWithID(headers, requestID)) 311 } 312 313 func convertHeaderToMetaWithID(headers http.Header, requestID string) metadata.MD { 314 meta := make(metadata.MD, len(headers)+1) 315 meta[reqIDKey] = []string{requestID} 316 317 // Metadata keys must be in lowe case 318 for k, v := range headers { 319 meta[strings.ToLower(k)] = v 320 } 321 322 return meta 323 } 324 325 // creates a context with propagated request metadata as well as requestID 326 func retrieveMetadata( 327 streamCtx context.Context, 328 instrumentOpts instrument.Options, 329 ) context.Context { 330 md, ok := metadata.FromIncomingContext(streamCtx) 331 id := "unknown" 332 if ok { 333 ids := md[reqIDKey] 334 if len(ids) == 1 { 335 id = ids[0] 336 } 337 } 338 339 return logging.NewContextWithID(streamCtx, id, instrumentOpts) 340 } 341 342 func decodeFetchRequest( 343 req *rpc.FetchRequest, 344 ) (*storage.FetchQuery, error) { 345 tags, err := decodeTagMatchers(req.GetTagMatchers()) 346 if err != nil { 347 return nil, err 348 } 349 350 return &storage.FetchQuery{ 351 TagMatchers: tags, 352 Start: toTime(req.Start), 353 End: toTime(req.End), 354 }, nil 355 } 356 357 func decodeTagMatchers(rpcMatchers *rpc.TagMatchers) (models.Matchers, error) { 358 tagMatchers := rpcMatchers.GetTagMatchers() 359 matchers := make([]models.Matcher, len(tagMatchers)) 360 for i, matcher := range tagMatchers { 361 matchType, name, value := models.MatchType(matcher.GetType()), matcher.GetName(), matcher.GetValue() 362 mMatcher, err := models.NewMatcher(matchType, name, value) 363 if err != nil { 364 return matchers, err 365 } 366 367 matchers[i] = mMatcher 368 } 369 370 return models.Matchers(matchers), nil 371 } 372 373 func decodeFanoutOption(opt rpc.FanoutOption) (storage.FanoutOption, error) { 374 switch opt { 375 case rpc.FanoutOption_DEFAULT_OPTION: 376 return storage.FanoutDefault, nil 377 case rpc.FanoutOption_FORCE_DISABLED: 378 return storage.FanoutForceDisable, nil 379 case rpc.FanoutOption_FORCE_ENABLED: 380 return storage.FanoutForceEnable, nil 381 } 382 383 return 0, fmt.Errorf("unknown fanout option for proto encoding: %v", opt) 384 } 385 386 func decodeRestrictQueryOptionsByType( 387 p *rpc.RestrictQueryType, 388 ) (*storage.RestrictByType, error) { 389 if p == nil { 390 return nil, nil 391 } 392 393 result := &storage.RestrictByType{} 394 switch p.GetMetricsType() { 395 case rpcpb.MetricsType_UNAGGREGATED_METRICS_TYPE: 396 result.MetricsType = storagemetadata.UnaggregatedMetricsType 397 case rpcpb.MetricsType_AGGREGATED_METRICS_TYPE: 398 result.MetricsType = storagemetadata.AggregatedMetricsType 399 } 400 401 if p.GetMetricsStoragePolicy() != nil { 402 storagePolicy, err := policy.NewStoragePolicyFromProto( 403 p.MetricsStoragePolicy) 404 if err != nil { 405 return result, err 406 } 407 408 result.StoragePolicy = storagePolicy 409 } 410 411 if err := result.Validate(); err != nil { 412 return nil, err 413 414 } 415 416 return result, nil 417 } 418 419 func decodeRestrictQueryOptionsByTag( 420 p *rpc.RestrictQueryTags, 421 ) (*storage.RestrictByTag, error) { 422 if p == nil { 423 return nil, nil 424 } 425 426 matchers, err := decodeTagMatchers(p.GetRestrict()) 427 if err != nil { 428 return nil, err 429 } 430 431 return &storage.RestrictByTag{ 432 Restrict: matchers, 433 Strip: p.Strip, 434 }, nil 435 } 436 437 func decodeRestrictQueryOptions( 438 p *rpc.RestrictQueryOptions, 439 ) (*storage.RestrictQueryOptions, error) { 440 if p == nil { 441 return nil, nil 442 } 443 444 byType, err := decodeRestrictQueryOptionsByType(p.GetRestrictQueryType()) 445 if err != nil { 446 return nil, err 447 } 448 449 byTag, err := decodeRestrictQueryOptionsByTag(p.GetRestrictQueryTags()) 450 if err != nil { 451 return nil, err 452 } 453 454 return &storage.RestrictQueryOptions{ 455 RestrictByType: byType, 456 RestrictByTag: byTag, 457 }, nil 458 } 459 460 func decodeFetchOptions(rpcFetchOptions *rpc.FetchOptions) (*storage.FetchOptions, error) { 461 result := storage.NewFetchOptions() 462 result.Remote = true 463 if rpcFetchOptions == nil { 464 return result, nil 465 } 466 467 result.SeriesLimit = int(rpcFetchOptions.Limit) 468 unagg, err := decodeFanoutOption(rpcFetchOptions.GetUnaggregated()) 469 if err != nil { 470 return nil, err 471 } 472 473 agg, err := decodeFanoutOption(rpcFetchOptions.GetAggregated()) 474 if err != nil { 475 return nil, err 476 } 477 478 aggOpt, err := decodeFanoutOption(rpcFetchOptions.GetAggregatedOptimized()) 479 if err != nil { 480 return nil, err 481 } 482 483 result.FanoutOptions = &storage.FanoutOptions{ 484 FanoutUnaggregated: unagg, 485 FanoutAggregated: agg, 486 FanoutAggregatedOptimized: aggOpt, 487 } 488 489 if v := rpcFetchOptions.Restrict; v != nil { 490 restrict, err := decodeRestrictQueryOptions(v) 491 if err != nil { 492 return nil, err 493 } 494 495 result.RestrictQueryOptions = restrict 496 } 497 498 if v := rpcFetchOptions.LookbackDuration; v > 0 { 499 duration := time.Duration(v) 500 result.LookbackDuration = &duration 501 } 502 503 result.Source = rpcFetchOptions.Source 504 return result, nil 505 } 506 507 func encodeResolutions(res []time.Duration) []int64 { 508 encoded := make([]int64, 0, len(res)) 509 for _, r := range res { 510 encoded = append(encoded, int64(r)) 511 } 512 513 return encoded 514 } 515 516 func encodeResultMetadata(meta block.ResultMetadata) *rpc.ResultMetadata { 517 warnings := make([]*rpc.Warning, 0, len(meta.Warnings)) 518 for _, warn := range meta.Warnings { 519 warnings = append(warnings, &rpc.Warning{ 520 Name: []byte(warn.Name), 521 Message: []byte(warn.Message), 522 }) 523 } 524 525 return &rpc.ResultMetadata{ 526 Exhaustive: meta.Exhaustive, 527 Warnings: warnings, 528 Resolutions: encodeResolutions(meta.Resolutions), 529 } 530 } 531 532 func decodeResolutions(res []int64) []time.Duration { 533 decoded := make([]time.Duration, 0, len(res)) 534 for _, d := range res { 535 decoded = append(decoded, time.Duration(d)) 536 } 537 538 return decoded 539 } 540 541 func decodeResultMetadata(meta *rpc.ResultMetadata) block.ResultMetadata { 542 rpcWarnings := meta.GetWarnings() 543 warnings := make([]block.Warning, 0, len(rpcWarnings)) 544 for _, warn := range rpcWarnings { 545 warnings = append(warnings, block.Warning{ 546 Name: string(warn.Name), 547 Message: string(warn.Message), 548 }) 549 } 550 551 return block.ResultMetadata{ 552 Exhaustive: meta.Exhaustive, 553 Warnings: warnings, 554 Resolutions: decodeResolutions(meta.GetResolutions()), 555 } 556 }