github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/executor_pkg_test.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package interlock
    15  
    16  import (
    17  	"context"
    18  	"crypto/tls"
    19  
    20  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    21  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    22  	"github.com/whtcorpsinc/BerolinaSQL/auth"
    23  	. "github.com/whtcorpsinc/check"
    24  	"github.com/whtcorpsinc/failpoint"
    25  	causetutil "github.com/whtcorpsinc/milevadb/causet/soliton"
    26  	"github.com/whtcorpsinc/milevadb/config"
    27  	"github.com/whtcorpsinc/milevadb/memex"
    28  	"github.com/whtcorpsinc/milevadb/soliton"
    29  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    30  	"github.com/whtcorpsinc/milevadb/soliton/memory"
    31  	"github.com/whtcorpsinc/milevadb/soliton/mock"
    32  	"github.com/whtcorpsinc/milevadb/soliton/ranger"
    33  	"github.com/whtcorpsinc/milevadb/stochastikctx/variable"
    34  	"github.com/whtcorpsinc/milevadb/types"
    35  )
    36  
    37  var _ = Suite(&testInterDircSuite{})
    38  var _ = SerialSuites(&testInterDircSerialSuite{})
    39  
    40  // Note: it's a tricky way to export the `inspectionSummaryMemrules` and `inspectionMemrules` for unit test but invisible for normal code
    41  var (
    42  	InspectionSummaryMemrules = inspectionSummaryMemrules
    43  	InspectionMemrules        = inspectionMemrules
    44  )
    45  
    46  type testInterDircSuite struct {
    47  }
    48  
    49  type testInterDircSerialSuite struct {
    50  }
    51  
    52  // mockStochastikManager is a mocked stochastik manager which is used for test.
    53  type mockStochastikManager struct {
    54  	PS []*soliton.ProcessInfo
    55  }
    56  
    57  // ShowProcessList implements the StochastikManager.ShowProcessList interface.
    58  func (msm *mockStochastikManager) ShowProcessList() map[uint64]*soliton.ProcessInfo {
    59  	ret := make(map[uint64]*soliton.ProcessInfo)
    60  	for _, item := range msm.PS {
    61  		ret[item.ID] = item
    62  	}
    63  	return ret
    64  }
    65  
    66  func (msm *mockStochastikManager) GetProcessInfo(id uint64) (*soliton.ProcessInfo, bool) {
    67  	for _, item := range msm.PS {
    68  		if item.ID == id {
    69  			return item, true
    70  		}
    71  	}
    72  	return &soliton.ProcessInfo{}, false
    73  }
    74  
    75  // Kill implements the StochastikManager.Kill interface.
    76  func (msm *mockStochastikManager) Kill(cid uint64, query bool) {
    77  
    78  }
    79  
    80  func (msm *mockStochastikManager) UFIDelateTLSConfig(cfg *tls.Config) {
    81  }
    82  
    83  func (s *testInterDircSuite) TestShowProcessList(c *C) {
    84  	// Compose schemaReplicant.
    85  	names := []string{"Id", "User", "Host", "EDB", "Command", "Time", "State", "Info"}
    86  	ftypes := []byte{allegrosql.TypeLonglong, allegrosql.TypeVarchar, allegrosql.TypeVarchar,
    87  		allegrosql.TypeVarchar, allegrosql.TypeVarchar, allegrosql.TypeLong, allegrosql.TypeVarchar, allegrosql.TypeString}
    88  	schemaReplicant := buildSchema(names, ftypes)
    89  
    90  	// Compose a mocked stochastik manager.
    91  	ps := make([]*soliton.ProcessInfo, 0, 1)
    92  	pi := &soliton.ProcessInfo{
    93  		ID:      0,
    94  		User:    "test",
    95  		Host:    "127.0.0.1",
    96  		EDB:     "test",
    97  		Command: 't',
    98  		State:   1,
    99  		Info:    "",
   100  	}
   101  	ps = append(ps, pi)
   102  	sm := &mockStochastikManager{
   103  		PS: ps,
   104  	}
   105  	sctx := mock.NewContext()
   106  	sctx.SetStochastikManager(sm)
   107  	sctx.GetStochastikVars().User = &auth.UserIdentity{Username: "test"}
   108  
   109  	// Compose interlock.
   110  	e := &ShowInterDirc{
   111  		baseInterlockingDirectorate: newBaseInterlockingDirectorate(sctx, schemaReplicant, 0),
   112  		Tp:                          ast.ShowProcessList,
   113  	}
   114  
   115  	ctx := context.Background()
   116  	err := e.Open(ctx)
   117  	c.Assert(err, IsNil)
   118  
   119  	chk := newFirstChunk(e)
   120  	it := chunk.NewIterator4Chunk(chk)
   121  	// Run test and check results.
   122  	for _, p := range ps {
   123  		err = e.Next(context.Background(), chk)
   124  		c.Assert(err, IsNil)
   125  		for event := it.Begin(); event != it.End(); event = it.Next() {
   126  			c.Assert(event.GetUint64(0), Equals, p.ID)
   127  		}
   128  	}
   129  	err = e.Next(context.Background(), chk)
   130  	c.Assert(err, IsNil)
   131  	c.Assert(chk.NumEvents(), Equals, 0)
   132  	err = e.Close()
   133  	c.Assert(err, IsNil)
   134  }
   135  
   136  func buildSchema(names []string, ftypes []byte) *memex.Schema {
   137  	schemaReplicant := memex.NewSchema(make([]*memex.DeferredCauset, 0, len(names))...)
   138  	for i := range names {
   139  		defCaus := &memex.DeferredCauset{
   140  			UniqueID: int64(i),
   141  		}
   142  		// User varchar as the default return defCausumn type.
   143  		tp := allegrosql.TypeVarchar
   144  		if len(ftypes) != 0 && ftypes[0] != allegrosql.TypeUnspecified {
   145  			tp = ftypes[0]
   146  		}
   147  		fieldType := types.NewFieldType(tp)
   148  		fieldType.Flen, fieldType.Decimal = allegrosql.GetDefaultFieldLengthAndDecimal(tp)
   149  		fieldType.Charset, fieldType.DefCauslate = types.DefaultCharsetForType(tp)
   150  		defCaus.RetType = fieldType
   151  		schemaReplicant.Append(defCaus)
   152  	}
   153  	return schemaReplicant
   154  }
   155  
   156  func (s *testInterDircSuite) TestBuildEkvRangesForIndexJoinWithoutCwc(c *C) {
   157  	indexRanges := make([]*ranger.Range, 0, 6)
   158  	indexRanges = append(indexRanges, generateIndexRange(1, 1, 1, 1, 1))
   159  	indexRanges = append(indexRanges, generateIndexRange(1, 1, 2, 1, 1))
   160  	indexRanges = append(indexRanges, generateIndexRange(1, 1, 2, 1, 2))
   161  	indexRanges = append(indexRanges, generateIndexRange(1, 1, 3, 1, 1))
   162  	indexRanges = append(indexRanges, generateIndexRange(2, 1, 1, 1, 1))
   163  	indexRanges = append(indexRanges, generateIndexRange(2, 1, 2, 1, 1))
   164  
   165  	joinKeyEvents := make([]*indexJoinLookUpContent, 0, 5)
   166  	joinKeyEvents = append(joinKeyEvents, &indexJoinLookUpContent{keys: generateCausetSlice(1, 1)})
   167  	joinKeyEvents = append(joinKeyEvents, &indexJoinLookUpContent{keys: generateCausetSlice(1, 2)})
   168  	joinKeyEvents = append(joinKeyEvents, &indexJoinLookUpContent{keys: generateCausetSlice(2, 1)})
   169  	joinKeyEvents = append(joinKeyEvents, &indexJoinLookUpContent{keys: generateCausetSlice(2, 2)})
   170  	joinKeyEvents = append(joinKeyEvents, &indexJoinLookUpContent{keys: generateCausetSlice(2, 3)})
   171  
   172  	keyOff2IdxOff := []int{1, 3}
   173  	ctx := mock.NewContext()
   174  	ekvRanges, err := buildEkvRangesForIndexJoin(ctx, 0, 0, joinKeyEvents, indexRanges, keyOff2IdxOff, nil)
   175  	c.Assert(err, IsNil)
   176  	// Check the ekvRanges is in order.
   177  	for i, ekvRange := range ekvRanges {
   178  		c.Assert(ekvRange.StartKey.Cmp(ekvRange.EndKey) < 0, IsTrue)
   179  		if i > 0 {
   180  			c.Assert(ekvRange.StartKey.Cmp(ekvRanges[i-1].EndKey) >= 0, IsTrue)
   181  		}
   182  	}
   183  }
   184  
   185  func generateIndexRange(vals ...int64) *ranger.Range {
   186  	lowCausets := generateCausetSlice(vals...)
   187  	highCausets := make([]types.Causet, len(vals))
   188  	copy(highCausets, lowCausets)
   189  	return &ranger.Range{LowVal: lowCausets, HighVal: highCausets}
   190  }
   191  
   192  func generateCausetSlice(vals ...int64) []types.Causet {
   193  	datums := make([]types.Causet, len(vals))
   194  	for i, val := range vals {
   195  		datums[i].SetInt64(val)
   196  	}
   197  	return datums
   198  }
   199  
   200  func (s *testInterDircSuite) TestGetFieldsFromLine(c *C) {
   201  	tests := []struct {
   202  		input    string
   203  		expected []string
   204  	}{
   205  		{
   206  			`"1","a string","100.20"`,
   207  			[]string{"1", "a string", "100.20"},
   208  		},
   209  		{
   210  			`"2","a string containing a , comma","102.20"`,
   211  			[]string{"2", "a string containing a , comma", "102.20"},
   212  		},
   213  		{
   214  			`"3","a string containing a \" quote","102.20"`,
   215  			[]string{"3", "a string containing a \" quote", "102.20"},
   216  		},
   217  		{
   218  			`"4","a string containing a \", quote and comma","102.20"`,
   219  			[]string{"4", "a string containing a \", quote and comma", "102.20"},
   220  		},
   221  		// Test some escape char.
   222  		{
   223  			`"\0\b\n\r\t\Z\\\  \c\'\""`,
   224  			[]string{string([]byte{0, '\b', '\n', '\r', '\t', 26, '\\', ' ', ' ', 'c', '\'', '"'})},
   225  		},
   226  		// Test mixed.
   227  		{
   228  			`"123",456,"\t7890",abcd`,
   229  			[]string{"123", "456", "\t7890", "abcd"},
   230  		},
   231  	}
   232  
   233  	ldInfo := LoadDataInfo{
   234  		FieldsInfo: &ast.FieldsClause{
   235  			Enclosed:   '"',
   236  			Terminated: ",",
   237  		},
   238  	}
   239  
   240  	for _, test := range tests {
   241  		got, err := ldInfo.getFieldsFromLine([]byte(test.input))
   242  		c.Assert(err, IsNil, Commentf("failed: %s", test.input))
   243  		assertEqualStrings(c, got, test.expected)
   244  	}
   245  
   246  	_, err := ldInfo.getFieldsFromLine([]byte(`1,a string,100.20`))
   247  	c.Assert(err, IsNil)
   248  }
   249  
   250  func assertEqualStrings(c *C, got []field, expect []string) {
   251  	c.Assert(len(got), Equals, len(expect))
   252  	for i := 0; i < len(got); i++ {
   253  		c.Assert(string(got[i].str), Equals, expect[i])
   254  	}
   255  }
   256  
   257  func (s *testInterDircSerialSuite) TestSortSpillDisk(c *C) {
   258  	defer config.RestoreFunc()()
   259  	config.UFIDelateGlobal(func(conf *config.Config) {
   260  		conf.OOMUseTmpStorage = true
   261  		conf.MemQuotaQuery = 1
   262  	})
   263  	c.Assert(failpoint.Enable("github.com/whtcorpsinc/milevadb/interlock/testSortedEventContainerSpill", "return(true)"), IsNil)
   264  	defer func() {
   265  		c.Assert(failpoint.Disable("github.com/whtcorpsinc/milevadb/interlock/testSortedEventContainerSpill"), IsNil)
   266  	}()
   267  	ctx := mock.NewContext()
   268  	ctx.GetStochastikVars().InitChunkSize = variable.DefMaxChunkSize
   269  	ctx.GetStochastikVars().MaxChunkSize = variable.DefMaxChunkSize
   270  	ctx.GetStochastikVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1)
   271  	cas := &sortCase{rows: 2048, orderByIdx: []int{0, 1}, ndvs: []int{0, 0}, ctx: ctx}
   272  	opt := mockDataSourceParameters{
   273  		schemaReplicant: memex.NewSchema(cas.defCausumns()...),
   274  		rows:            cas.rows,
   275  		ctx:             cas.ctx,
   276  		ndvs:            cas.ndvs,
   277  	}
   278  	dataSource := buildMockDataSource(opt)
   279  	exec := &SortInterDirc{
   280  		baseInterlockingDirectorate: newBaseInterlockingDirectorate(cas.ctx, dataSource.schemaReplicant, 0, dataSource),
   281  		ByItems:                     make([]*causetutil.ByItems, 0, len(cas.orderByIdx)),
   282  		schemaReplicant:             dataSource.schemaReplicant,
   283  	}
   284  	for _, idx := range cas.orderByIdx {
   285  		exec.ByItems = append(exec.ByItems, &causetutil.ByItems{Expr: cas.defCausumns()[idx]})
   286  	}
   287  	tmpCtx := context.Background()
   288  	chk := newFirstChunk(exec)
   289  	dataSource.prepareChunks()
   290  	err := exec.Open(tmpCtx)
   291  	c.Assert(err, IsNil)
   292  	for {
   293  		err = exec.Next(tmpCtx, chk)
   294  		c.Assert(err, IsNil)
   295  		if chk.NumEvents() == 0 {
   296  			break
   297  		}
   298  	}
   299  	// Test only 1 partition and all data in memory.
   300  	c.Assert(len(exec.partitionList), Equals, 1)
   301  	c.Assert(exec.partitionList[0].AlreadySpilledSafeForTest(), Equals, false)
   302  	c.Assert(exec.partitionList[0].NumEvent(), Equals, 2048)
   303  	err = exec.Close()
   304  	c.Assert(err, IsNil)
   305  
   306  	ctx.GetStochastikVars().StmtCtx.MemTracker = memory.NewTracker(-1, 1)
   307  	dataSource.prepareChunks()
   308  	err = exec.Open(tmpCtx)
   309  	c.Assert(err, IsNil)
   310  	for {
   311  		err = exec.Next(tmpCtx, chk)
   312  		c.Assert(err, IsNil)
   313  		if chk.NumEvents() == 0 {
   314  			break
   315  		}
   316  	}
   317  	// Test 2 partitions and all data in disk.
   318  	// Now spilling is in parallel.
   319  	// Maybe the second add() will called before spilling, depends on
   320  	// Golang goroutine scheduling. So the result has two possibilities.
   321  	if len(exec.partitionList) == 2 {
   322  		c.Assert(len(exec.partitionList), Equals, 2)
   323  		c.Assert(exec.partitionList[0].AlreadySpilledSafeForTest(), Equals, true)
   324  		c.Assert(exec.partitionList[1].AlreadySpilledSafeForTest(), Equals, true)
   325  		c.Assert(exec.partitionList[0].NumEvent(), Equals, 1024)
   326  		c.Assert(exec.partitionList[1].NumEvent(), Equals, 1024)
   327  	} else {
   328  		c.Assert(len(exec.partitionList), Equals, 1)
   329  		c.Assert(exec.partitionList[0].AlreadySpilledSafeForTest(), Equals, true)
   330  		c.Assert(exec.partitionList[0].NumEvent(), Equals, 2048)
   331  	}
   332  
   333  	err = exec.Close()
   334  	c.Assert(err, IsNil)
   335  
   336  	ctx.GetStochastikVars().StmtCtx.MemTracker = memory.NewTracker(-1, 24000)
   337  	dataSource.prepareChunks()
   338  	err = exec.Open(tmpCtx)
   339  	c.Assert(err, IsNil)
   340  	for {
   341  		err = exec.Next(tmpCtx, chk)
   342  		c.Assert(err, IsNil)
   343  		if chk.NumEvents() == 0 {
   344  			break
   345  		}
   346  	}
   347  	// Test only 1 partition but spill disk.
   348  	c.Assert(len(exec.partitionList), Equals, 1)
   349  	c.Assert(exec.partitionList[0].AlreadySpilledSafeForTest(), Equals, true)
   350  	c.Assert(exec.partitionList[0].NumEvent(), Equals, 2048)
   351  	err = exec.Close()
   352  	c.Assert(err, IsNil)
   353  
   354  	// Test partition nums.
   355  	ctx = mock.NewContext()
   356  	ctx.GetStochastikVars().InitChunkSize = variable.DefMaxChunkSize
   357  	ctx.GetStochastikVars().MaxChunkSize = variable.DefMaxChunkSize
   358  	ctx.GetStochastikVars().StmtCtx.MemTracker = memory.NewTracker(-1, 16864*50)
   359  	ctx.GetStochastikVars().StmtCtx.MemTracker.Consume(16864 * 45)
   360  	cas = &sortCase{rows: 20480, orderByIdx: []int{0, 1}, ndvs: []int{0, 0}, ctx: ctx}
   361  	opt = mockDataSourceParameters{
   362  		schemaReplicant: memex.NewSchema(cas.defCausumns()...),
   363  		rows:            cas.rows,
   364  		ctx:             cas.ctx,
   365  		ndvs:            cas.ndvs,
   366  	}
   367  	dataSource = buildMockDataSource(opt)
   368  	exec = &SortInterDirc{
   369  		baseInterlockingDirectorate: newBaseInterlockingDirectorate(cas.ctx, dataSource.schemaReplicant, 0, dataSource),
   370  		ByItems:                     make([]*causetutil.ByItems, 0, len(cas.orderByIdx)),
   371  		schemaReplicant:             dataSource.schemaReplicant,
   372  	}
   373  	for _, idx := range cas.orderByIdx {
   374  		exec.ByItems = append(exec.ByItems, &causetutil.ByItems{Expr: cas.defCausumns()[idx]})
   375  	}
   376  	tmpCtx = context.Background()
   377  	chk = newFirstChunk(exec)
   378  	dataSource.prepareChunks()
   379  	err = exec.Open(tmpCtx)
   380  	c.Assert(err, IsNil)
   381  	for {
   382  		err = exec.Next(tmpCtx, chk)
   383  		c.Assert(err, IsNil)
   384  		if chk.NumEvents() == 0 {
   385  			break
   386  		}
   387  	}
   388  	// Don't spill too many partitions.
   389  	c.Assert(len(exec.partitionList) <= 4, IsTrue)
   390  	err = exec.Close()
   391  	c.Assert(err, IsNil)
   392  }