github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/prometheus/handleroptions/headers.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 handleroptions 22 23 import ( 24 "encoding/json" 25 "fmt" 26 "net/http" 27 "strings" 28 29 "github.com/m3db/m3/src/query/block" 30 "github.com/m3db/m3/src/query/storage" 31 "github.com/m3db/m3/src/x/headers" 32 ) 33 34 // ReturnedDataLimited is info about whether data was limited by a query. 35 type ReturnedDataLimited struct { 36 Series int 37 Datapoints int 38 39 // Total series is the total number of series which maybe be >= Series. 40 // Truncation happens at the series-level to avoid presenting partial series 41 // and so this value is useful for indicating how many series would have 42 // been rendered without limiting either series or datapoints. 43 TotalSeries int 44 45 // Limited signals that the results returned were 46 // limited by either series or datapoint limits. 47 Limited bool 48 } 49 50 // ReturnedMetadataLimited is info about whether data was limited by a query. 51 type ReturnedMetadataLimited struct { 52 // Results is the total amount of rendered results. 53 Results int 54 // TotalResults is the total amount of metadata results. 55 TotalResults int 56 // Limited signals that the results returned were limited by a limit. 57 Limited bool 58 } 59 60 // Waiting is info about an operation waiting for permits. 61 type Waiting struct { 62 // WaitedIndex counts how many times index querying had to wait for permits. 63 WaitedIndex int `json:"waitedIndex"` 64 // WaitedSeriesRead counts how many times series being read had to wait for permits. 65 WaitedSeriesRead int `json:"waitedSeriesRead"` 66 } 67 68 // WaitedAny returns whether any waiting occurred. 69 func (w Waiting) WaitedAny() bool { 70 return w.WaitedIndex > 0 || w.WaitedSeriesRead > 0 71 } 72 73 // AddDBResultResponseHeaders adds response headers based on metadata 74 // and fetch options related to the database result. 75 func AddDBResultResponseHeaders( 76 w http.ResponseWriter, 77 meta block.ResultMetadata, 78 fetchOpts *storage.FetchOptions, 79 ) error { 80 if fetchOpts != nil { 81 w.Header().Set(headers.TimeoutHeader, fetchOpts.Timeout.String()) 82 } 83 84 waiting := Waiting{ 85 WaitedIndex: meta.WaitedIndex, 86 WaitedSeriesRead: meta.WaitedSeriesRead, 87 } 88 89 // NB: only add count headers if present. 90 if meta.FetchedSeriesCount > 0 { 91 w.Header().Add(headers.FetchedSeriesCount, fmt.Sprint(meta.FetchedSeriesCount)) 92 } 93 94 // Merge all of the metadata by name results into `merged` and report on that. 95 merged := meta.MetadataByNameMerged() 96 97 if merged.Aggregated > 0 { 98 w.Header().Add(headers.FetchedAggregatedSeriesCount, fmt.Sprint(merged.Aggregated)) 99 } 100 101 if merged.Unaggregated > 0 { 102 w.Header().Add(headers.FetchedUnaggregatedSeriesCount, fmt.Sprint(merged.Unaggregated)) 103 } 104 105 if merged.NoSamples > 0 { 106 w.Header().Add(headers.FetchedSeriesNoSamplesCount, fmt.Sprint(merged.NoSamples)) 107 } 108 109 if merged.WithSamples > 0 { 110 w.Header().Add(headers.FetchedSeriesWithSamplesCount, fmt.Sprint(merged.WithSamples)) 111 } 112 113 if namespaces := meta.GetNamespaces(); len(namespaces) > 0 { 114 w.Header().Add(headers.NamespacesHeader, strings.Join(namespaces, ",")) 115 } 116 117 if meta.FetchedResponses > 0 { 118 w.Header().Add(headers.FetchedResponsesHeader, fmt.Sprint(meta.FetchedResponses)) 119 } 120 121 if meta.FetchedBytesEstimate > 0 { 122 w.Header().Add(headers.FetchedBytesEstimateHeader, fmt.Sprint(meta.FetchedBytesEstimate)) 123 } 124 125 // Also report the top metadata by name, in JSON. 126 if fetchOpts != nil && fetchOpts.MaxMetricMetadataStats > 0 { 127 if stats := meta.TopMetadataByName(fetchOpts.MaxMetricMetadataStats); len(stats) > 0 { 128 js, err := json.Marshal(stats) 129 if err != nil { 130 return err 131 } 132 w.Header().Add(headers.MetricStats, string(js)) 133 } 134 } 135 136 if meta.FetchedMetadataCount > 0 { 137 w.Header().Add(headers.FetchedMetadataCount, fmt.Sprint(meta.FetchedMetadataCount)) 138 } 139 140 if waiting.WaitedAny() { 141 s, err := json.Marshal(waiting) 142 if err != nil { 143 return err 144 } 145 w.Header().Add(headers.WaitedHeader, string(s)) 146 } 147 148 ex := meta.Exhaustive 149 warns := len(meta.Warnings) 150 if !ex { 151 warns++ 152 } 153 154 if warns == 0 { 155 return nil 156 } 157 158 warnings := make([]string, 0, warns) 159 if !ex { 160 warnings = append(warnings, headers.LimitHeaderSeriesLimitApplied) 161 } 162 163 for _, warn := range meta.Warnings { 164 warnings = append(warnings, warn.Header()) 165 } 166 167 w.Header().Set(headers.LimitHeader, strings.Join(warnings, ",")) 168 169 return nil 170 } 171 172 // AddReturnedLimitResponseHeaders adds headers related to hitting 173 // limits on the allowed amount of data that can be returned to the client. 174 func AddReturnedLimitResponseHeaders( 175 w http.ResponseWriter, 176 returnedDataLimited *ReturnedDataLimited, 177 returnedMetadataLimited *ReturnedMetadataLimited, 178 ) error { 179 if limited := returnedDataLimited; limited != nil { 180 s, err := json.Marshal(limited) 181 if err != nil { 182 return err 183 } 184 w.Header().Add(headers.ReturnedDataLimitedHeader, string(s)) 185 } 186 if limited := returnedMetadataLimited; limited != nil { 187 s, err := json.Marshal(limited) 188 if err != nil { 189 return err 190 } 191 w.Header().Add(headers.ReturnedMetadataLimitedHeader, string(s)) 192 } 193 194 return nil 195 }