github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/querytracker/querytracker_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 querytracker
    18  
    19  import (
    20  	"os"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  
    25  	"github.com/siglens/siglens/pkg/config"
    26  	. "github.com/siglens/siglens/pkg/segment/structs"
    27  	"github.com/siglens/siglens/pkg/segment/utils"
    28  )
    29  
    30  // used only by tests to reset tracked info
    31  func resetInternalQTInfo() {
    32  	localPersistentAggs = make(map[string]*PersistentAggregation)
    33  	localPersistentQueries = make(map[string]*PersistentSearchNode)
    34  	allNodesPQsSorted = []*PersistentSearchNode{}
    35  	allPersistentAggsSorted = []*PersistentAggregation{}
    36  }
    37  
    38  func Test_GetQTUsageInfo(t *testing.T) {
    39  	resetInternalQTInfo()
    40  	config.SetPQSEnabled(true)
    41  	qVal, err := utils.CreateDtypeEnclosure("iOS", 0)
    42  	assert.Nil(t, err)
    43  
    44  	sNode := &SearchNode{
    45  		AndSearchConditions: &SearchCondition{
    46  			SearchQueries: []*SearchQuery{
    47  				{
    48  					ExpressionFilter: &SearchExpression{
    49  						LeftSearchInput:  &SearchExpressionInput{ColumnName: "os"},
    50  						FilterOp:         utils.Equals,
    51  						RightSearchInput: &SearchExpressionInput{ColumnValue: qVal},
    52  					},
    53  					SearchType: SimpleExpression,
    54  				},
    55  			},
    56  		},
    57  		NodeType: ColumnValueQuery,
    58  	}
    59  	sNodeHash := GetHashForQuery(sNode)
    60  	tableName := []string{"ind-tab-v1"}
    61  	for i := 0; i < 90; i++ {
    62  		UpdateQTUsage(tableName, sNode, nil)
    63  	}
    64  
    65  	us, err := GetQTUsageInfo(tableName, sNode)
    66  	assert.Nil(t, err)
    67  	assert.NotNil(t, us)
    68  	expected := uint32(90)
    69  	assert.Equal(t, expected, us.TotalUsage, "expected %v usagecount but got %v", expected, us.TotalUsage)
    70  
    71  	ok, err := IsQueryPersistent(tableName, sNode)
    72  	assert.Nil(t, err)
    73  	assert.Equal(t, true, ok, "query was supposed to be persistent")
    74  
    75  	sNode.AndSearchConditions.SearchQueries[0].ExpressionFilter.LeftSearchInput.ColumnName = "os2"
    76  	ok, err = IsQueryPersistent(tableName, sNode)
    77  	assert.Nil(t, err)
    78  	assert.Equal(t, false, ok, "query was supposed to be NOT persistent")
    79  
    80  	res, err := GetTopNPersistentSearches(tableName[0], 0)
    81  	assert.Nil(t, err)
    82  	assert.Equal(t, 1, len(res), "There should be 1 persistent query but got=%v", len(res))
    83  
    84  	wildCard, err := utils.CreateDtypeEnclosure("*", 0)
    85  	assert.Nil(t, err)
    86  
    87  	matchAllOne := &SearchNode{
    88  		AndSearchConditions: &SearchCondition{
    89  			SearchQueries: []*SearchQuery{
    90  				{
    91  					ExpressionFilter: &SearchExpression{
    92  						LeftSearchInput:  &SearchExpressionInput{ColumnName: "*"},
    93  						FilterOp:         utils.Equals,
    94  						RightSearchInput: &SearchExpressionInput{ColumnValue: wildCard},
    95  					},
    96  					SearchType: SimpleExpression,
    97  				},
    98  			},
    99  		},
   100  		NodeType: MatchAllQuery,
   101  	}
   102  
   103  	UpdateQTUsage(tableName, matchAllOne, nil)
   104  
   105  	_, err = GetQTUsageInfo(tableName, matchAllOne)
   106  	assert.NotNil(t, err, "match all should not be added to query tracker")
   107  
   108  	ok, err = IsQueryPersistent(tableName, matchAllOne)
   109  	assert.Nil(t, err)
   110  	assert.Equal(t, false, ok, "query is  not persistent")
   111  
   112  	matchAllHash := GetHashForQuery(matchAllOne)
   113  	res, err = GetTopNPersistentSearches(tableName[0], 0)
   114  	assert.Nil(t, err)
   115  	assert.Equal(t, 1, len(res), "There should be 1 persistent query but got=%v", len(res))
   116  	assert.Contains(t, res, sNodeHash, "sNodeHash=%v should exist in result=%+v", sNodeHash, res)
   117  	assert.NotContains(t, res, matchAllHash, "matchAllHash=%v should not exist in result=%+v", matchAllHash, res)
   118  	assert.Equal(t, ColumnValueQuery, res[sNodeHash].NodeType, "non match all result %+v should exist", res[sNodeHash])
   119  	assert.Nil(t, res[matchAllHash], "match all result %+v should exist", res[matchAllHash])
   120  }
   121  
   122  func Test_ReadWriteQTUsage(t *testing.T) {
   123  	resetInternalQTInfo()
   124  	config.SetPQSEnabled(true)
   125  	config.SetSSInstanceName("qt-test")
   126  	err := config.InitDerivedConfig("test")
   127  	assert.NoError(t, err)
   128  	_ = os.RemoveAll("./ingestnodes")
   129  	_ = os.RemoveAll("./querynodes")
   130  
   131  	qVal, err := utils.CreateDtypeEnclosure("batch-101", 0)
   132  	assert.Nil(t, err)
   133  	sNode := &SearchNode{
   134  		AndSearchConditions: &SearchCondition{
   135  			SearchQueries: []*SearchQuery{
   136  				{
   137  					ExpressionFilter: &SearchExpression{
   138  						LeftSearchInput:  &SearchExpressionInput{ColumnName: "batch"},
   139  						FilterOp:         utils.Equals,
   140  						RightSearchInput: &SearchExpressionInput{ColumnValue: qVal},
   141  					},
   142  					SearchType: SimpleExpression,
   143  				},
   144  			},
   145  		},
   146  		NodeType: ColumnValueQuery,
   147  	}
   148  
   149  	sNodeHash := GetHashForQuery(sNode)
   150  	tableName := []string{"test-1"}
   151  
   152  	aggs := &QueryAggregators{
   153  		GroupByRequest: &GroupByRequest{
   154  			MeasureOperations: []*MeasureAggregator{
   155  				{MeasureCol: "col3", MeasureFunc: utils.Avg},
   156  				{MeasureCol: "col4", MeasureFunc: utils.Count},
   157  			},
   158  			GroupByColumns: []string{"col1", "col2"},
   159  		},
   160  	}
   161  	aggsHash := GetHashForAggs(aggs)
   162  	UpdateQTUsage(tableName, sNode, aggs)
   163  
   164  	flushPQueriesToDisk()
   165  	resetInternalQTInfo()
   166  	readSavedQueryInfo()
   167  	assert.Len(t, allNodesPQsSorted, 1)
   168  	assert.Len(t, localPersistentQueries, 1)
   169  	assert.Len(t, allPersistentAggsSorted, 1)
   170  	assert.Len(t, localPersistentAggs, 1)
   171  
   172  	assert.Contains(t, localPersistentQueries, sNodeHash)
   173  	assert.Contains(t, localPersistentAggs, aggsHash)
   174  }
   175  
   176  func Test_GetTopPersistentAggs(t *testing.T) {
   177  	resetInternalQTInfo()
   178  	config.SetPQSEnabled(true)
   179  	aggs := &QueryAggregators{
   180  		GroupByRequest: &GroupByRequest{
   181  			MeasureOperations: []*MeasureAggregator{
   182  				{MeasureCol: "col3", MeasureFunc: utils.Avg},
   183  				{MeasureCol: "col4", MeasureFunc: utils.Count},
   184  			},
   185  			GroupByColumns: []string{"col1", "col2"},
   186  		},
   187  	}
   188  	tableName := []string{"test-1"}
   189  	UpdateQTUsage(tableName, nil, aggs)
   190  	grpCols, measure := GetTopPersistentAggs("test-2")
   191  	assert.Len(t, grpCols, 0)
   192  	assert.Len(t, measure, 0)
   193  
   194  	grpCols, measure = GetTopPersistentAggs("test-1")
   195  	assert.Len(t, grpCols, 2)
   196  	assert.Len(t, measure, 2)
   197  	assert.Contains(t, grpCols, "col1")
   198  	assert.Contains(t, grpCols, "col2")
   199  
   200  	var mCol3 string
   201  	var mCol4 string
   202  	for mcol := range measure {
   203  		if mcol == "col3" {
   204  			mCol3 = mcol
   205  		} else if mcol == "col4" {
   206  			mCol4 = mcol
   207  		}
   208  	}
   209  	assert.NotEqual(t, "", mCol3)
   210  	assert.NotEqual(t, "", mCol4)
   211  
   212  	aggs2 := &QueryAggregators{
   213  		GroupByRequest: &GroupByRequest{
   214  			MeasureOperations: []*MeasureAggregator{
   215  				{MeasureCol: "col3", MeasureFunc: utils.Cardinality},
   216  			},
   217  			GroupByColumns: []string{"col3", "col2"},
   218  		},
   219  	}
   220  	UpdateQTUsage(tableName, nil, aggs2)
   221  	grpCols, measure = GetTopPersistentAggs("test-1")
   222  	assert.Len(t, grpCols, 3)
   223  	assert.Equal(t, "col2", grpCols[0], "only col2 exists in both usages, so it should be first")
   224  	var mCol3_1 string
   225  	mCol4 = ""
   226  	for m := range measure {
   227  		if m == "col3" {
   228  			if mCol3_1 == "" {
   229  				mCol3_1 = m
   230  			}
   231  		} else if m == "col4" {
   232  			mCol4 = m
   233  		}
   234  	}
   235  	assert.NotEqual(t, "", mCol3_1)
   236  	assert.NotEqual(t, "", mCol4)
   237  }
   238  
   239  func Test_GetTopPersistentAggs_Jaeger(t *testing.T) {
   240  	resetInternalQTInfo()
   241  	config.SetPQSEnabled(true)
   242  	aggs := &QueryAggregators{
   243  		GroupByRequest: &GroupByRequest{
   244  			MeasureOperations: []*MeasureAggregator{
   245  				{MeasureCol: "col3", MeasureFunc: utils.Avg},
   246  				{MeasureCol: "col4", MeasureFunc: utils.Count},
   247  			},
   248  			GroupByColumns: []string{"col1", "col2"},
   249  		},
   250  	}
   251  	tableName := []string{"jaeger-1"}
   252  	UpdateQTUsage(tableName, nil, aggs)
   253  
   254  	grpCols, measure := GetTopPersistentAggs("jaeger-1")
   255  	assert.Len(t, grpCols, 5)
   256  	assert.Len(t, measure, 3)
   257  	assert.Contains(t, grpCols, "col1")
   258  	assert.Contains(t, grpCols, "col2")
   259  	assert.Contains(t, grpCols, "traceID")
   260  	assert.Contains(t, grpCols, "serviceName")
   261  	assert.Contains(t, grpCols, "operationName")
   262  
   263  	var mCol3 string
   264  	var mCol4 string
   265  	for mcol := range measure {
   266  		if mcol == "col3" {
   267  			mCol3 = mcol
   268  		} else if mcol == "col4" {
   269  			mCol4 = mcol
   270  		}
   271  	}
   272  	assert.NotEqual(t, "", mCol3)
   273  	assert.NotEqual(t, "", mCol4)
   274  
   275  	aggs2 := &QueryAggregators{
   276  		GroupByRequest: &GroupByRequest{
   277  			MeasureOperations: []*MeasureAggregator{
   278  				{MeasureCol: "startTime", MeasureFunc: utils.Max},
   279  			},
   280  			GroupByColumns: []string{"col3", "col2"},
   281  		},
   282  	}
   283  	UpdateQTUsage(tableName, nil, aggs2)
   284  	grpCols, measure = GetTopPersistentAggs("jaeger-1")
   285  	assert.Len(t, grpCols, 6)
   286  	assert.Equal(t, "traceID", grpCols[0], "traceID exists in both usages, so it should be first")
   287  	var mCol3_1 string
   288  	mCol4 = ""
   289  	for m := range measure {
   290  		if m == "col3" {
   291  			if mCol3_1 == "" {
   292  				mCol3_1 = m
   293  			}
   294  		} else if m == "col4" {
   295  			mCol4 = m
   296  		}
   297  	}
   298  	assert.NotEqual(t, "", mCol3_1)
   299  	assert.NotEqual(t, "", mCol4)
   300  }
   301  
   302  func Test_AggsHasher(t *testing.T) {
   303  	resetInternalQTInfo()
   304  	config.SetPQSEnabled(true)
   305  	aggs1 := &QueryAggregators{
   306  		GroupByRequest: &GroupByRequest{
   307  			MeasureOperations: []*MeasureAggregator{
   308  				{MeasureCol: "col3", MeasureFunc: utils.Avg},
   309  				{MeasureCol: "col4", MeasureFunc: utils.Count},
   310  			},
   311  			GroupByColumns: []string{"col1", "col2"},
   312  		},
   313  	}
   314  	id1 := GetHashForAggs(aggs1)
   315  	aggs2 := &QueryAggregators{
   316  		GroupByRequest: &GroupByRequest{
   317  			MeasureOperations: []*MeasureAggregator{
   318  				{MeasureCol: "col4", MeasureFunc: utils.Count},
   319  				{MeasureCol: "col3", MeasureFunc: utils.Avg},
   320  			},
   321  			GroupByColumns: []string{"col2", "col1"},
   322  		},
   323  	}
   324  	id2 := GetHashForAggs(aggs2)
   325  	assert.Equal(t, id1, id2)
   326  
   327  	aggs3 := &QueryAggregators{
   328  		GroupByRequest: &GroupByRequest{
   329  			MeasureOperations: []*MeasureAggregator{
   330  				{MeasureCol: "col4", MeasureFunc: utils.Count},
   331  				{MeasureCol: "col3", MeasureFunc: utils.Count},
   332  			},
   333  			GroupByColumns: []string{"col2", "col1"},
   334  		},
   335  	}
   336  	id3 := GetHashForAggs(aggs3)
   337  	assert.NotEqual(t, id1, id3)
   338  }
   339  
   340  func Test_PostPqsClear(t *testing.T) {
   341  	resetInternalQTInfo()
   342  	config.SetPQSEnabled(true)
   343  	qVal, err := utils.CreateDtypeEnclosure("iOS", 0)
   344  	assert.Nil(t, err)
   345  
   346  	sNode := &SearchNode{
   347  		AndSearchConditions: &SearchCondition{
   348  			SearchQueries: []*SearchQuery{
   349  				{
   350  					ExpressionFilter: &SearchExpression{
   351  						LeftSearchInput:  &SearchExpressionInput{ColumnName: "os"},
   352  						FilterOp:         utils.Equals,
   353  						RightSearchInput: &SearchExpressionInput{ColumnValue: qVal},
   354  					},
   355  					SearchType: SimpleExpression,
   356  				},
   357  			},
   358  		},
   359  		NodeType: ColumnValueQuery,
   360  	}
   361  	pqid := GetHashForQuery(sNode)
   362  	tableName := []string{"test-1"}
   363  	assert.NotNil(t, tableName)
   364  	UpdateQTUsage(tableName, sNode, nil)
   365  
   366  	expected := map[string]interface{}{
   367  		"promoted_aggregations": make(map[string]int),
   368  		"promoted_searches":     make(map[string]int),
   369  		"total_tracked_queries": 0,
   370  	}
   371  	var pqsSummary, clearPqsSummary map[string]interface{}
   372  	pqsSummary = getPQSSummary()
   373  	assert.NotNil(t, pqsSummary)
   374  	totalQueries := pqsSummary["total_tracked_queries"]
   375  	assert.NotNil(t, totalQueries)
   376  	totalQueriesInt, ok := totalQueries.(int)
   377  	assert.Equal(t, true, ok, "converting total persistent queries to int did not work")
   378  	assert.Equal(t, totalQueriesInt, 1, "There should be 1 persistent query but got=%v", totalQueriesInt)
   379  
   380  	pqsinfo := getPqsById(pqid)
   381  	assert.NotNil(t, pqsinfo)
   382  	ClearPqs()
   383  	clearPqsSummary = getPQSSummary()
   384  	assert.NotNil(t, clearPqsSummary)
   385  	assert.Equal(t, expected, clearPqsSummary, "the pqsinfo was supposed to be cleared")
   386  }