github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/storage/m3/convert_test.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 m3 22 23 import ( 24 "bytes" 25 "fmt" 26 "math" 27 "sort" 28 "testing" 29 "time" 30 31 "github.com/m3db/m3/src/dbnode/encoding" 32 "github.com/m3db/m3/src/query/block" 33 "github.com/m3db/m3/src/query/models" 34 "github.com/m3db/m3/src/query/test" 35 xtime "github.com/m3db/m3/src/x/time" 36 37 "github.com/stretchr/testify/assert" 38 "github.com/stretchr/testify/require" 39 ) 40 41 var ( 42 Start = time.Now().Truncate(time.Hour) 43 blockSize = time.Minute * 6 44 nan = math.NaN() 45 ) 46 47 func generateIterators( 48 t testing.TB, 49 stepSize time.Duration, 50 ) ( 51 []encoding.SeriesIterator, 52 models.Bounds, 53 ) { 54 datapoints := [][][]test.Datapoint{ 55 { 56 []test.Datapoint{}, 57 []test.Datapoint{ 58 {Value: 1, Offset: 0}, 59 {Value: 2, Offset: (time.Minute * 1) + time.Second}, 60 {Value: 3, Offset: (time.Minute * 2)}, 61 {Value: 4, Offset: (time.Minute * 3)}, 62 {Value: 5, Offset: (time.Minute * 4)}, 63 {Value: 6, Offset: (time.Minute * 5)}, 64 }, 65 []test.Datapoint{ 66 {Value: 7, Offset: time.Minute * 0}, 67 {Value: 8, Offset: time.Minute * 5}, 68 }, 69 []test.Datapoint{ 70 {Value: 9, Offset: time.Minute * 4}, 71 }, 72 }, 73 { 74 []test.Datapoint{ 75 {Value: 10, Offset: (time.Minute * 2)}, 76 {Value: 20, Offset: (time.Minute * 3)}, 77 }, 78 []test.Datapoint{}, 79 []test.Datapoint{ 80 {Value: 30, Offset: time.Minute}, 81 }, 82 []test.Datapoint{ 83 {Value: 40, Offset: time.Second}, 84 }, 85 }, 86 { 87 []test.Datapoint{ 88 {Value: 100, Offset: (time.Minute * 3)}, 89 }, 90 []test.Datapoint{ 91 {Value: 200, Offset: (time.Minute * 3)}, 92 }, 93 []test.Datapoint{ 94 {Value: 300, Offset: (time.Minute * 3)}, 95 }, 96 []test.Datapoint{ 97 {Value: 400, Offset: (time.Minute * 3)}, 98 }, 99 []test.Datapoint{ 100 {Value: 500, Offset: 0}, 101 }, 102 }, 103 } 104 105 iters := make([]encoding.SeriesIterator, len(datapoints)) 106 var ( 107 iter encoding.SeriesIterator 108 bounds models.Bounds 109 start = Start 110 ) 111 for i, dps := range datapoints { 112 iter, bounds = buildCustomIterator(t, i, start, stepSize, dps) 113 iters[i] = iter 114 } 115 116 return iters, bounds 117 } 118 119 func buildCustomIterator( 120 t testing.TB, 121 i int, 122 start time.Time, 123 stepSize time.Duration, 124 dps [][]test.Datapoint, 125 ) ( 126 encoding.SeriesIterator, 127 models.Bounds, 128 ) { 129 iter, bounds, err := test.BuildCustomIterator( 130 dps, 131 map[string]string{"a": "b", "c": fmt.Sprint(i)}, 132 fmt.Sprintf("abc%d", i), "namespace", 133 xtime.ToUnixNano(start), 134 blockSize, stepSize, 135 ) 136 require.NoError(t, err) 137 return iter, bounds 138 } 139 140 // verifies that given sub-bounds are valid, and returns the index of the 141 // generated block being checked. 142 func verifyBoundsAndGetBlockIndex(t *testing.T, bounds, sub models.Bounds) int { 143 require.Equal(t, bounds.StepSize, sub.StepSize) 144 require.Equal(t, blockSize, sub.Duration) 145 diff := sub.Start.Sub(bounds.Start) 146 require.Equal(t, 0, int(diff%blockSize)) 147 return int(diff / blockSize) 148 } 149 150 type ascByName []block.SeriesMeta 151 152 func (m ascByName) Len() int { return len(m) } 153 func (m ascByName) Less(i, j int) bool { 154 return bytes.Compare(m[i].Name, m[j].Name) == -1 155 } 156 func (m ascByName) Swap(i, j int) { m[i], m[j] = m[j], m[i] } 157 158 func verifyMetas( 159 t *testing.T, 160 _ int, 161 meta block.Metadata, 162 metas []block.SeriesMeta, 163 ) { 164 require.Equal(t, 0, meta.Tags.Len()) 165 sort.Sort(ascByName(metas)) 166 for i, m := range metas { 167 assert.Equal(t, fmt.Sprintf("abc%d", i), string(m.Name)) 168 require.Equal(t, 2, m.Tags.Len()) 169 170 val, found := m.Tags.Get([]byte("a")) 171 assert.True(t, found) 172 assert.Equal(t, "b", string(val)) 173 174 val, found = m.Tags.Get([]byte("c")) 175 assert.True(t, found) 176 require.Equal(t, fmt.Sprint(i), string(val)) 177 } 178 } 179 180 // NB: blocks are not necessarily generated in order; last block may be the first 181 // one in the returned array. This is fine since they are processed independently 182 // and are put back together at the read step, or at any temporal functions in 183 // the execution pipeline. 184 func generateBlocks( 185 t testing.TB, 186 stepSize time.Duration, 187 opts Options, 188 ) ([]block.Block, models.Bounds) { 189 iterators, bounds := generateIterators(t, stepSize) 190 res, err := iterToFetchResult(iterators) 191 require.NoError(t, err) 192 blocks, err := ConvertM3DBSeriesIterators( 193 res, 194 bounds, 195 opts, 196 ) 197 require.NoError(t, err) 198 return blocks, bounds 199 } 200 201 func TestUpdateTimeBySteps(t *testing.T) { 202 var tests = []struct { 203 stepSize, blockSize, expected time.Duration 204 }{ 205 {time.Minute * 15, time.Hour, time.Hour}, 206 {time.Minute * 14, time.Hour, time.Minute * 70}, 207 {time.Minute * 13, time.Hour, time.Minute * 65}, 208 {time.Minute * 59, time.Hour, time.Minute * 118}, 209 } 210 211 for _, tt := range tests { 212 updateDuration := blockDuration(tt.blockSize, tt.stepSize) 213 assert.Equal(t, tt.expected/time.Minute, updateDuration/time.Minute) 214 } 215 } 216 217 func TestPadSeriesBlocks(t *testing.T) { 218 blockSize := time.Hour 219 start := xtime.Now().Truncate(blockSize) 220 readOffset := time.Minute 221 itStart := start.Add(readOffset) 222 223 var tests = []struct { 224 blockStart xtime.UnixNano 225 stepSize time.Duration 226 expectedStart xtime.UnixNano 227 expectedStartTwo xtime.UnixNano 228 }{ 229 {start, time.Minute * 30, itStart, start.Add(61 * time.Minute)}, 230 { 231 start.Add(blockSize), 232 time.Minute * 30, 233 start.Add(61 * time.Minute), 234 start.Add(121 * time.Minute), 235 }, 236 // step 0: start + 1 , start + 37 237 // step 1: start + 73 , start + 109 238 // step 2: start + 145 239 // step 3: start + 181, start + 217 240 // step 4: start + 253, ... 241 { 242 start.Add(blockSize * 3), 243 time.Minute * 36, 244 start.Add(181 * time.Minute), 245 start.Add(253 * time.Minute), 246 }, 247 // step 0: start + 1 , start + 38 248 // step 1: start + 75 , start + 112 249 // step 2: start + 149 250 // step 3: start + 186 251 { 252 start.Add(blockSize * 2), 253 time.Minute * 37, 254 start.Add(149 * time.Minute), 255 start.Add(186 * time.Minute), 256 }, 257 // step 0: start + 1 , start + 12 , start + 23, 258 // start + 34 , start + 45 , start + 56 259 // step 1: start + 67 , start + 78 , start + 89 260 // start + 100, start + 111 261 // step 2: start + 122 ... 262 { 263 start.Add(blockSize * 1), 264 time.Minute * 11, 265 start.Add(67 * time.Minute), 266 start.Add(122 * time.Minute), 267 }, 268 } 269 270 for _, tt := range tests { 271 blocks := seriesBlocks{ 272 { 273 blockStart: tt.blockStart, 274 blockSize: blockSize, 275 replicas: []encoding.MultiReaderIterator{}, 276 }, 277 { 278 blockStart: tt.blockStart.Add(blockSize), 279 blockSize: blockSize, 280 replicas: []encoding.MultiReaderIterator{}, 281 }, 282 } 283 284 updated := updateSeriesBlockStarts(blocks, tt.stepSize, itStart) 285 require.Equal(t, 2, len(updated)) 286 assert.Equal(t, tt.blockStart, updated[0].blockStart) 287 assert.Equal(t, tt.expectedStart, updated[0].readStart) 288 289 assert.Equal(t, tt.blockStart.Add(blockSize), updated[1].blockStart) 290 assert.Equal(t, tt.expectedStartTwo, updated[1].readStart) 291 } 292 }