github.com/m3db/m3@v1.5.0/src/dbnode/ts/writes/write_batch_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 writes 22 23 import ( 24 "bytes" 25 "errors" 26 "fmt" 27 "sync" 28 "testing" 29 30 "github.com/m3db/m3/src/x/checked" 31 "github.com/m3db/m3/src/x/ident" 32 "github.com/m3db/m3/src/x/pool" 33 "github.com/m3db/m3/src/x/serialize" 34 xtime "github.com/m3db/m3/src/x/time" 35 36 "github.com/stretchr/testify/require" 37 ) 38 39 const ( 40 batchSize = 2 41 ) 42 43 var ( 44 namespace = ident.StringID("namespace") 45 writes = []testWrite{ 46 { 47 id: ident.StringID("series1"), 48 tagIter: ident.NewTagsIterator(ident.NewTags( 49 ident.Tag{ 50 Name: ident.StringID("name1"), 51 Value: ident.StringID("value1"), 52 })), 53 timestamp: xtime.Now(), 54 value: 0, 55 unit: xtime.Nanosecond, 56 annotation: []byte("annotation1"), 57 }, 58 { 59 id: ident.StringID("series2"), 60 tagIter: ident.NewTagsIterator(ident.NewTags( 61 ident.Tag{ 62 Name: ident.StringID("name2"), 63 Value: ident.StringID("value2"), 64 })), 65 timestamp: xtime.Now(), 66 value: 1, 67 unit: xtime.Nanosecond, 68 annotation: []byte("annotation2s"), 69 }, 70 { 71 id: ident.StringID("series3"), 72 tagIter: ident.NewTagsIterator(ident.NewTags( 73 ident.Tag{ 74 Name: ident.StringID("name3"), 75 Value: ident.StringID("value3"), 76 })), 77 timestamp: xtime.Now(), 78 value: 2, 79 unit: xtime.Nanosecond, 80 annotation: []byte("annotation3s"), 81 }, 82 } 83 ) 84 85 var ( 86 testTagEncodingPool = struct { 87 once sync.Once 88 pool serialize.TagEncoderPool 89 }{ 90 pool: serialize.NewTagEncoderPool(serialize.NewTagEncoderOptions(), 91 pool.NewObjectPoolOptions().SetSize(1)), 92 } 93 ) 94 95 func getTagEncoder() serialize.TagEncoder { 96 testTagEncodingPool.once.Do(func() { 97 testTagEncodingPool.pool.Init() 98 }) 99 return testTagEncodingPool.pool.Get() 100 } 101 102 type testWrite struct { 103 id ident.ID 104 tagIter ident.TagIterator 105 timestamp xtime.UnixNano 106 value float64 107 unit xtime.Unit 108 annotation []byte 109 } 110 111 func (w testWrite) encodedTags(t *testing.T) checked.Bytes { 112 encoder := getTagEncoder() 113 require.NoError(t, encoder.Encode(w.tagIter.Duplicate())) 114 data, ok := encoder.Data() 115 require.True(t, ok) 116 return data 117 } 118 119 func TestBatchWriterAddAndIter(t *testing.T) { 120 writeBatch := NewWriteBatch(batchSize, namespace, nil) 121 122 for i, write := range writes { 123 writeBatch.Add( 124 i, 125 write.id, 126 write.timestamp, 127 write.value, 128 write.unit, 129 write.annotation) 130 } 131 132 // Make sure all the data is there 133 assertDataPresent(t, writes, writeBatch) 134 } 135 136 func TestBatchWriterAddTaggedAndIter(t *testing.T) { 137 writeBatch := NewWriteBatch(batchSize, namespace, nil) 138 139 for i, write := range writes { 140 writeBatch.AddTagged( 141 i, 142 write.id, 143 write.encodedTags(t).Bytes(), 144 write.timestamp, 145 write.value, 146 write.unit, 147 write.annotation) 148 } 149 150 // Make sure all the data is there 151 assertDataPresent(t, writes, writeBatch) 152 } 153 154 func TestBatchWriterSetSeries(t *testing.T) { 155 writeBatch := NewWriteBatch(batchSize, namespace, nil) 156 157 for i, write := range writes { 158 writeBatch.AddTagged( 159 i, 160 write.id, 161 write.encodedTags(t).Bytes(), 162 write.timestamp, 163 write.value, 164 write.unit, 165 write.annotation) 166 } 167 168 // Set the outcome 169 iter := writeBatch.Iter() 170 for i, curr := range iter { 171 if i == 0 { 172 // skip the first write. 173 writeBatch.SetSkipWrite(i) 174 continue 175 } 176 177 var ( 178 currWrite = curr.Write 179 currSeries = currWrite.Series 180 newSeries = currSeries 181 ) 182 newSeries.ID = ident.StringID(fmt.Sprint(i)) 183 184 if i == len(iter)-1 { 185 // Set skip for this to true; it should revert to not skipping after 186 // SetOutcome called below. 187 err := errors.New("some-error") 188 writeBatch.SetError(i, err) 189 } else { 190 writeBatch.SetSeries(i, newSeries) 191 } 192 } 193 194 iter = writeBatch.Iter() 195 require.Equal(t, 3, len(iter)) 196 197 require.True(t, iter[0].SkipWrite) 198 199 for j, curr := range iter[1:] { 200 var ( 201 currWrite = curr.Write 202 currSeries = currWrite.Series 203 i = j + 1 204 ) 205 if i == len(iter)-1 { 206 require.Equal(t, errors.New("some-error"), curr.Err) 207 require.True(t, curr.SkipWrite) 208 continue 209 } 210 211 require.Equal(t, fmt.Sprint(i), string(currSeries.ID.String())) 212 require.False(t, curr.SkipWrite) 213 214 require.NoError(t, curr.Err) 215 } 216 } 217 218 func TestWriteBatchReset(t *testing.T) { 219 var ( 220 numResets = 10 221 writeBatch = NewWriteBatch(batchSize, namespace, nil) 222 ) 223 224 for i := 0; i < numResets; i++ { 225 writeBatch.Reset(batchSize, namespace) 226 for _, write := range writes { 227 writeBatch.Add( 228 i, 229 write.id, 230 write.timestamp, 231 write.value, 232 write.unit, 233 write.annotation) 234 } 235 236 // Make sure all the data is there 237 assertDataPresent(t, writes, writeBatch) 238 } 239 } 240 241 func assertDataPresent(t *testing.T, writes []testWrite, batchWriter WriteBatch) { 242 for _, write := range writes { 243 var ( 244 iter = batchWriter.Iter() 245 found = false 246 ) 247 248 for _, currWriteBatch := range iter { 249 var ( 250 currWrite = currWriteBatch.Write 251 currSeries = currWrite.Series 252 ) 253 254 if currSeries.ID.Equal(write.id) { 255 require.Equal(t, namespace, currWrite.Series.Namespace) 256 require.Equal(t, write.timestamp, currWrite.Datapoint.TimestampNanos) 257 require.Equal(t, write.value, currWrite.Datapoint.Value) 258 require.Equal(t, write.unit, currWrite.Unit) 259 require.True(t, bytes.Equal(write.annotation, currWrite.Annotation)) 260 found = true 261 break 262 } 263 } 264 265 require.True(t, found, fmt.Sprintf("expected to find series: %s", write.id)) 266 } 267 } 268 269 func TestBatchWriterFinalizer(t *testing.T) { 270 var ( 271 numEncodedTagsFinalized = 0 272 numAnnotationsFinalized = 0 273 numFinalized = 0 274 275 finalizeEncodedTagsFn = func(b []byte) { 276 numEncodedTagsFinalized++ 277 } 278 finalizeAnnotationFn = func(b []byte) { 279 numAnnotationsFinalized++ 280 } 281 finalizeFn = func(b WriteBatch) { 282 numFinalized++ 283 } 284 ) 285 286 writeBatch := NewWriteBatch(batchSize, namespace, finalizeFn) 287 writeBatch.SetFinalizeEncodedTagsFn(finalizeEncodedTagsFn) 288 writeBatch.SetFinalizeAnnotationFn(finalizeAnnotationFn) 289 290 for i, write := range writes { 291 writeBatch.AddTagged( 292 i, 293 write.id, 294 write.encodedTags(t).Bytes(), 295 write.timestamp, 296 write.value, 297 write.unit, 298 write.annotation) 299 } 300 301 require.Equal(t, 3, len(writeBatch.Iter())) 302 writeBatch.Finalize() 303 require.Equal(t, 0, len(writeBatch.Iter())) 304 require.Equal(t, 1, numFinalized) 305 require.Equal(t, 3, numEncodedTagsFinalized) 306 require.Equal(t, 3, numAnnotationsFinalized) 307 }