github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/writer/segwriter_test.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package writer 18 19 import ( 20 "fmt" 21 "strconv" 22 23 // "encoding/json" 24 25 "github.com/bits-and-blooms/bloom/v3" 26 "github.com/stretchr/testify/assert" 27 28 // "reflect" 29 "testing" 30 31 "github.com/siglens/siglens/pkg/config" 32 "github.com/siglens/siglens/pkg/segment/structs" 33 . "github.com/siglens/siglens/pkg/segment/structs" 34 . "github.com/siglens/siglens/pkg/segment/utils" 35 "github.com/siglens/siglens/pkg/utils" 36 ) 37 38 var rangeIndex map[string]*Numbers 39 40 func Test_isTimeRangeOverlapping(t *testing.T) { 41 baseStart := uint64(4) 42 baseEnd := uint64(8) 43 cases := []struct { 44 name string 45 start uint64 46 end uint64 47 expected bool 48 }{ 49 { 50 name: "Completely before base range", 51 start: 1, 52 end: 2, 53 expected: false, 54 }, 55 { 56 name: "Touching start", 57 start: 1, 58 end: 4, 59 expected: true, 60 }, 61 { 62 name: "End within base range", 63 start: 1, 64 end: 6, 65 expected: true, 66 }, 67 { 68 name: "Start and end both within base range", 69 start: 5, 70 end: 7, 71 expected: true, 72 }, 73 { 74 name: "Touching end", 75 start: 6, 76 end: 8, 77 expected: true, 78 }, 79 { 80 name: "Start within base range", 81 start: 6, 82 end: 9, 83 expected: true, 84 }, 85 { 86 name: "Completely after base range", 87 start: 9, 88 end: 12, 89 expected: false, 90 }, 91 } 92 for _, c := range cases { 93 t.Run(c.name, func(t *testing.T) { 94 isOverlapping := isTimeRangeOverlapping(baseStart, baseEnd, c.start, c.end) 95 96 if isOverlapping != c.expected { 97 t.Errorf("Failed: Expected=%v, got=%v, test name=%v", c.expected, isOverlapping, c.name) 98 } 99 }) 100 } 101 } 102 103 func isTimeRangeOverlapping(start1, end1, start2, end2 uint64) bool { 104 return utils.Max(start1, start2) <= utils.Min(end1, end2) 105 } 106 107 func Test_getBaseSegDir(t *testing.T) { 108 config.InitializeDefaultConfig() 109 virtualTableName := "evts" 110 streamid := "10005995996882630313" 111 nextsuff_idx := uint64(1) 112 basedir := getActiveBaseSegDir(streamid, virtualTableName, nextsuff_idx) 113 assert.EqualValues(t, "data/"+config.GetHostID()+"/active/"+virtualTableName+"/"+streamid+"/1/", basedir) 114 } 115 116 func Test_getFinalBaseSegDir(t *testing.T) { 117 config.InitializeDefaultConfig() 118 virtualTableName := "evts" 119 streamid := "10005995996882630313" 120 nextsuff_idx := uint64(1) 121 basedir := getFinalBaseSegDir(streamid, virtualTableName, nextsuff_idx) 122 assert.EqualValues(t, "data/"+config.GetHostID()+"/final/"+virtualTableName+"/"+streamid+"/1/", basedir) 123 } 124 125 func Test_getNumberTypeAndVal(t *testing.T) { 126 cases := []struct { 127 input string 128 numType SS_IntUintFloatTypes 129 }{ 130 {`-99`, SS_INT8}, 131 {`99`, SS_UINT8}, 132 {`-128`, SS_INT8}, 133 {`127`, SS_UINT8}, 134 {`-129`, SS_INT16}, 135 {`128`, SS_UINT8}, 136 {`-32768`, SS_INT16}, 137 {`32767`, SS_UINT16}, 138 {`-32769`, SS_INT32}, 139 {`32769`, SS_UINT16}, 140 {`-2147483648`, SS_INT32}, 141 {`-2147483648`, SS_INT32}, 142 {`-2147483649`, SS_INT64}, 143 {`2147483649`, SS_UINT32}, 144 {`-0`, SS_UINT8}, 145 {`0`, SS_UINT8}, 146 {`0.0`, SS_UINT8}, 147 {`65535`, SS_UINT16}, 148 {`65536`, SS_UINT32}, 149 {`4294967295`, SS_UINT32}, 150 {`4294967296`, SS_UINT64}, 151 {`-4294967296`, SS_INT64}, 152 {`124294967296`, SS_UINT64}, 153 {`-124294967296`, SS_INT64}, 154 {`-12429496729600`, SS_INT64}, 155 {`12429496729600`, SS_UINT64}, 156 {`-124294967.29600`, SS_FLOAT64}, 157 {`124294967.29600`, SS_FLOAT64}, 158 } 159 160 for i, test := range cases { 161 numType, _, _, _ := GetNumberTypeAndVal(test.input) 162 assert.Equal(t, test.numType, numType, fmt.Sprintf("getNumberTypeAndVal testId: %d: Failed: actual: [%v], expected: [%v]", i+1, numType, test.numType)) 163 164 } 165 } 166 167 func getMin(key string) (interface{}, RangeNumType) { 168 ri := rangeIndex[key] 169 riNumType := ri.NumType 170 switch riNumType { 171 case RNT_UNSIGNED_INT: 172 return ri.Min_uint64, riNumType 173 case RNT_SIGNED_INT: 174 return ri.Min_int64, riNumType 175 case RNT_FLOAT64: 176 return ri.Min_float64, riNumType 177 } 178 return nil, 0 179 } 180 181 func getMax(key string) (interface{}, RangeNumType) { 182 ri := rangeIndex[key] 183 riNumType := ri.NumType 184 switch riNumType { 185 case RNT_UNSIGNED_INT: 186 return ri.Max_uint64, riNumType 187 case RNT_SIGNED_INT: 188 return ri.Max_int64, riNumType 189 case RNT_FLOAT64: 190 return ri.Max_float64, riNumType 191 } 192 return nil, 0 193 } 194 195 func Test_wrapperForUpdateRange(t *testing.T) { 196 rangeIndex = map[string]*Numbers{} 197 cases := []struct { 198 key string 199 numstr string 200 expectedMinVal interface{} 201 expectedMaxVal interface{} 202 expectedRangeNumType RangeNumType 203 }{ 204 {key: "ID", numstr: "1", expectedMinVal: 1, expectedMaxVal: 1, expectedRangeNumType: RNT_UNSIGNED_INT}, 205 {key: "ID", numstr: "-1", expectedMinVal: -1, expectedMaxVal: 1, expectedRangeNumType: RNT_SIGNED_INT}, 206 {key: "ID", numstr: "10", expectedMinVal: -1, expectedMaxVal: 10, expectedRangeNumType: RNT_SIGNED_INT}, 207 {key: "AMOUNT", numstr: "655", expectedMinVal: 655, expectedMaxVal: 655, expectedRangeNumType: RNT_UNSIGNED_INT}, 208 {key: "AMOUNT", numstr: "-6", expectedMinVal: -6, expectedMaxVal: 655, expectedRangeNumType: RNT_SIGNED_INT}, 209 {key: "AMOUNT", numstr: "-6.5", expectedMinVal: -6.5, expectedMaxVal: 655, expectedRangeNumType: RNT_FLOAT64}, 210 {key: "AMOUNT", numstr: "700", expectedMinVal: -6.5, expectedMaxVal: 700, expectedRangeNumType: RNT_FLOAT64}, 211 {key: "AMOUNT", numstr: "712.5", expectedMinVal: -6.5, expectedMaxVal: 712.5, expectedRangeNumType: RNT_FLOAT64}, 212 {key: "SCORE", numstr: "80.2", expectedMinVal: 80.2, expectedMaxVal: 80.2, expectedRangeNumType: RNT_FLOAT64}, 213 {key: "SCORE", numstr: "-10", expectedMinVal: -10, expectedMaxVal: 80.2, expectedRangeNumType: RNT_FLOAT64}, 214 {key: "SCORE", numstr: "-1.3", expectedMinVal: -10, expectedMaxVal: 80.2, expectedRangeNumType: RNT_FLOAT64}, 215 {key: "SCORE", numstr: "100", expectedMinVal: -10, expectedMaxVal: 100, expectedRangeNumType: RNT_FLOAT64}, 216 } 217 for i, test := range cases { 218 wrapperForUpdateRange(test.key, test.numstr, rangeIndex) 219 actualMinVal, numType := getMin(test.key) 220 actualMaxVal, _ := getMax(test.key) 221 switch numType { 222 case RNT_UNSIGNED_INT: 223 assert.EqualValues(t, test.expectedMinVal, actualMinVal.(uint64), fmt.Sprintf("Comparison failed, test=%v, expected=%v, actual=%v", i+1, test.expectedMinVal, actualMinVal)) 224 assert.EqualValues(t, test.expectedMaxVal, actualMaxVal.(uint64), fmt.Sprintf("Comparison failed, test=%v, expected=%v, actual=%v", i+1, test.expectedMaxVal, actualMaxVal)) 225 case RNT_SIGNED_INT: 226 assert.EqualValues(t, test.expectedMinVal, actualMinVal.(int64), fmt.Sprintf("Comparison failed, test=%v, expected=%v, actual=%v", i+1, test.expectedMinVal, actualMinVal)) 227 assert.EqualValues(t, test.expectedMaxVal, actualMaxVal.(int64), fmt.Sprintf("Comparison failed, test=%v, expected=%v, actual=%v", i+1, test.expectedMaxVal, actualMaxVal)) 228 case RNT_FLOAT64: 229 assert.EqualValues(t, test.expectedMinVal, actualMinVal.(float64), fmt.Sprintf("Comparison failed, test=%v, expected=%v, actual=%v", i+1, test.expectedMinVal, actualMinVal)) 230 assert.EqualValues(t, test.expectedMaxVal, actualMaxVal.(float64), fmt.Sprintf("Comparison failed, test=%v, expected=%v, actual=%v", i+1, test.expectedMaxVal, actualMaxVal)) 231 } 232 assert.EqualValues(t, test.expectedRangeNumType, numType, fmt.Sprintf("Comparison failed, test=%v, expected=%v, actual=%v", i+1, test.expectedRangeNumType, numType)) 233 234 } 235 } 236 237 func Test_addToBlockBloom(t *testing.T) { 238 cases := []struct { 239 fullWord []byte // full word to add 240 expectedMatches []string // expected words to pass bloom check 241 expectedAddCount uint32 // expected number of words added to the bloom 242 matchedToFail []string // words that should not pass bloom check 243 }{ 244 {fullWord: []byte("nosubword"), expectedMatches: []string{"nosubword"}, expectedAddCount: 1, matchedToFail: []string{"no"}}, 245 {fullWord: []byte("many sub words"), expectedMatches: []string{"many", "sub", "words", "many sub words"}, expectedAddCount: 4, matchedToFail: []string{"many sub"}}, 246 {fullWord: []byte(strconv.FormatBool(true)), expectedMatches: []string{"true"}, expectedAddCount: 1, matchedToFail: []string{"false"}}, 247 {fullWord: []byte("end whitespace "), expectedMatches: []string{"end", "whitespace", "end whitespace "}, expectedAddCount: 3, matchedToFail: []string{" "}}, 248 } 249 250 for i, test := range cases { 251 mockBloom := bloom.NewWithEstimates(uint(1000), BLOOM_COLL_PROBABILITY) 252 addedCount := addToBlockBloom(mockBloom, test.fullWord) 253 assert.Equal(t, addedCount, test.expectedAddCount) 254 255 for _, word := range test.expectedMatches { 256 assert.True(t, mockBloom.TestString(word), fmt.Sprintf("test=%v failed to find %+v in bloom", i, word)) 257 } 258 for _, word := range test.matchedToFail { 259 assert.False(t, mockBloom.TestString(word), fmt.Sprintf("test=%v found %+v in bloom, when should not happen", i, word)) 260 } 261 } 262 } 263 264 func Benchmark_wrapperForUpdateRange(b *testing.B) { 265 b.ReportAllocs() 266 b.ResetTimer() 267 rangeIndex = map[string]*Numbers{} 268 269 cases := []struct { 270 key string 271 numstr string 272 }{ 273 {"b", "1.456"}, 274 {"f", "12"}, 275 {"g", "51456"}, 276 {"h", "7551456"}, 277 {"i", "13887551456"}, 278 {"j", "-12"}, 279 {"k", "-200"}, 280 {"l", "-7551456"}, 281 {"n", "-1.323232"}, 282 } 283 for _, test := range cases { 284 for i := 0; i < b.N; i++ { 285 wrapperForUpdateRange(test.key, test.numstr, rangeIndex) 286 } 287 } 288 //Date 2/9/2021 Benchmark stats 289 //Benchmark_wrapperForUpdateRange-8 1224584 962.0 ns/op 96 B/op 2 allocs/op 290 291 } 292 func Benchmark_addToBloom(b *testing.B) { 293 294 exampleWord := []byte("abc def ghi jkl mnop") 295 mockBloom := bloom.NewWithEstimates(uint(1000), BLOOM_COLL_PROBABILITY) 296 297 for i := 0; i < b.N; i++ { 298 mockBloom.ClearAll() 299 addToBlockBloom(mockBloom, exampleWord) 300 } 301 } 302 303 func wrapperForUpdateRange(key string, numstr string, rangeIndex map[string]*structs.Numbers) { 304 numType, intVal, uintVal, fltVal := GetNumberTypeAndVal(numstr) 305 updateRangeIndex(key, rangeIndex, numType, intVal, uintVal, fltVal) 306 }