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  }