github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/split_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  	"bytes"
    18  	"encoding/binary"
    19  	"math"
    20  	"math/rand"
    21  	"sort"
    22  	"time"
    23  
    24  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    25  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    26  	. "github.com/whtcorpsinc/check"
    27  	"github.com/whtcorpsinc/milevadb/blockcodec"
    28  	"github.com/whtcorpsinc/milevadb/causet/blocks"
    29  	"github.com/whtcorpsinc/milevadb/causet/embedded"
    30  	"github.com/whtcorpsinc/milevadb/ekv"
    31  	"github.com/whtcorpsinc/milevadb/memex"
    32  	"github.com/whtcorpsinc/milevadb/soliton/mock"
    33  	"github.com/whtcorpsinc/milevadb/stochastikctx/stmtctx"
    34  	"github.com/whtcorpsinc/milevadb/types"
    35  )
    36  
    37  var _ = Suite(&testSplitIndex{})
    38  
    39  type testSplitIndex struct {
    40  }
    41  
    42  func (s *testSplitIndex) SetUpSuite(c *C) {
    43  }
    44  
    45  func (s *testSplitIndex) TearDownSuite(c *C) {
    46  }
    47  
    48  func (s *testSplitIndex) TestLongestCommonPrefixLen(c *C) {
    49  	cases := []struct {
    50  		s1 string
    51  		s2 string
    52  		l  int
    53  	}{
    54  		{"", "", 0},
    55  		{"", "a", 0},
    56  		{"a", "", 0},
    57  		{"a", "a", 1},
    58  		{"ab", "a", 1},
    59  		{"a", "ab", 1},
    60  		{"b", "ab", 0},
    61  		{"ba", "ab", 0},
    62  	}
    63  
    64  	for _, ca := range cases {
    65  		re := longestCommonPrefixLen([]byte(ca.s1), []byte(ca.s2))
    66  		c.Assert(re, Equals, ca.l)
    67  	}
    68  }
    69  
    70  func (s *testSplitIndex) TestgetStepValue(c *C) {
    71  	cases := []struct {
    72  		lower []byte
    73  		upper []byte
    74  		l     int
    75  		v     uint64
    76  	}{
    77  		{[]byte{}, []byte{}, 0, math.MaxUint64},
    78  		{[]byte{0}, []byte{128}, 0, binary.BigEndian.Uint64([]byte{128, 255, 255, 255, 255, 255, 255, 255})},
    79  		{[]byte{'a'}, []byte{'z'}, 0, binary.BigEndian.Uint64([]byte{'z' - 'a', 255, 255, 255, 255, 255, 255, 255})},
    80  		{[]byte("abc"), []byte{'z'}, 0, binary.BigEndian.Uint64([]byte{'z' - 'a', 255 - 'b', 255 - 'c', 255, 255, 255, 255, 255})},
    81  		{[]byte("abc"), []byte("xyz"), 0, binary.BigEndian.Uint64([]byte{'x' - 'a', 'y' - 'b', 'z' - 'c', 255, 255, 255, 255, 255})},
    82  		{[]byte("abc"), []byte("axyz"), 1, binary.BigEndian.Uint64([]byte{'x' - 'b', 'y' - 'c', 'z', 255, 255, 255, 255, 255})},
    83  		{[]byte("abc0123456"), []byte("xyz01234"), 0, binary.BigEndian.Uint64([]byte{'x' - 'a', 'y' - 'b', 'z' - 'c', 0, 0, 0, 0, 0})},
    84  	}
    85  
    86  	for _, ca := range cases {
    87  		l := longestCommonPrefixLen(ca.lower, ca.upper)
    88  		c.Assert(l, Equals, ca.l)
    89  		v0 := getStepValue(ca.lower[l:], ca.upper[l:], 1)
    90  		c.Assert(v0, Equals, ca.v)
    91  	}
    92  }
    93  
    94  func (s *testSplitIndex) TestSplitIndex(c *C) {
    95  	tbInfo := &perceptron.BlockInfo{
    96  		Name: perceptron.NewCIStr("t1"),
    97  		ID:   rand.Int63(),
    98  		DeferredCausets: []*perceptron.DeferredCausetInfo{
    99  			{
   100  				Name:         perceptron.NewCIStr("c0"),
   101  				ID:           1,
   102  				Offset:       1,
   103  				DefaultValue: 0,
   104  				State:        perceptron.StatePublic,
   105  				FieldType:    *types.NewFieldType(allegrosql.TypeLong),
   106  			},
   107  		},
   108  	}
   109  	idxDefCauss := []*perceptron.IndexDeferredCauset{{Name: tbInfo.DeferredCausets[0].Name, Offset: 0, Length: types.UnspecifiedLength}}
   110  	idxInfo := &perceptron.IndexInfo{
   111  		ID:              2,
   112  		Name:            perceptron.NewCIStr("idx1"),
   113  		Block:           perceptron.NewCIStr("t1"),
   114  		DeferredCausets: idxDefCauss,
   115  		State:           perceptron.StatePublic,
   116  	}
   117  	firstIdxInfo0 := idxInfo.Clone()
   118  	firstIdxInfo0.ID = 1
   119  	firstIdxInfo0.Name = perceptron.NewCIStr("idx")
   120  	tbInfo.Indices = []*perceptron.IndexInfo{firstIdxInfo0, idxInfo}
   121  
   122  	// Test for int index.
   123  	// range is 0 ~ 100, and split into 10 region.
   124  	// So 10 regions range is like below, left close right open interval:
   125  	// region1: [-inf ~ 10)
   126  	// region2: [10 ~ 20)
   127  	// region3: [20 ~ 30)
   128  	// region4: [30 ~ 40)
   129  	// region5: [40 ~ 50)
   130  	// region6: [50 ~ 60)
   131  	// region7: [60 ~ 70)
   132  	// region8: [70 ~ 80)
   133  	// region9: [80 ~ 90)
   134  	// region10: [90 ~ +inf)
   135  	ctx := mock.NewContext()
   136  	e := &SplitIndexRegionInterDirc{
   137  		baseInterlockingDirectorate: newBaseInterlockingDirectorate(ctx, nil, 0),
   138  		blockInfo:                   tbInfo,
   139  		indexInfo:                   idxInfo,
   140  		lower:                       []types.Causet{types.NewCauset(0)},
   141  		upper:                       []types.Causet{types.NewCauset(100)},
   142  		num:                         10,
   143  	}
   144  	valueList, err := e.getSplitIdxKeys()
   145  	sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 })
   146  	c.Assert(err, IsNil)
   147  	c.Assert(len(valueList), Equals, e.num+1)
   148  
   149  	cases := []struct {
   150  		value        int
   151  		lessEqualIdx int
   152  	}{
   153  		{-1, 0},
   154  		{0, 0},
   155  		{1, 0},
   156  		{10, 1},
   157  		{11, 1},
   158  		{20, 2},
   159  		{21, 2},
   160  		{31, 3},
   161  		{41, 4},
   162  		{51, 5},
   163  		{61, 6},
   164  		{71, 7},
   165  		{81, 8},
   166  		{91, 9},
   167  		{100, 9},
   168  		{1000, 9},
   169  	}
   170  
   171  	index := blocks.NewIndex(tbInfo.ID, tbInfo, idxInfo)
   172  	for _, ca := range cases {
   173  		// test for minInt64 handle
   174  		idxValue, _, err := index.GenIndexKey(ctx.GetStochastikVars().StmtCtx, []types.Causet{types.NewCauset(ca.value)}, ekv.IntHandle(math.MinInt64), nil)
   175  		c.Assert(err, IsNil)
   176  		idx := searchLessEqualIdx(valueList, idxValue)
   177  		c.Assert(idx, Equals, ca.lessEqualIdx, Commentf("%#v", ca))
   178  
   179  		// Test for max int64 handle.
   180  		idxValue, _, err = index.GenIndexKey(ctx.GetStochastikVars().StmtCtx, []types.Causet{types.NewCauset(ca.value)}, ekv.IntHandle(math.MaxInt64), nil)
   181  		c.Assert(err, IsNil)
   182  		idx = searchLessEqualIdx(valueList, idxValue)
   183  		c.Assert(idx, Equals, ca.lessEqualIdx, Commentf("%#v", ca))
   184  	}
   185  	// Test for varchar index.
   186  	// range is a ~ z, and split into 26 region.
   187  	// So 26 regions range is like below:
   188  	// region1: [-inf ~ b)
   189  	// region2: [b ~ c)
   190  	// .
   191  	// .
   192  	// .
   193  	// region26: [y ~ +inf)
   194  	e.lower = []types.Causet{types.NewCauset("a")}
   195  	e.upper = []types.Causet{types.NewCauset("z")}
   196  	e.num = 26
   197  	// change index defCausumn type to varchar
   198  	tbInfo.DeferredCausets[0].FieldType = *types.NewFieldType(allegrosql.TypeVarchar)
   199  
   200  	valueList, err = e.getSplitIdxKeys()
   201  	sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 })
   202  	c.Assert(err, IsNil)
   203  	c.Assert(len(valueList), Equals, e.num+1)
   204  
   205  	cases2 := []struct {
   206  		value        string
   207  		lessEqualIdx int
   208  	}{
   209  		{"", 0},
   210  		{"a", 0},
   211  		{"abcde", 0},
   212  		{"b", 1},
   213  		{"bzzzz", 1},
   214  		{"c", 2},
   215  		{"czzzz", 2},
   216  		{"z", 25},
   217  		{"zabcd", 25},
   218  	}
   219  
   220  	for _, ca := range cases2 {
   221  		// test for minInt64 handle
   222  		idxValue, _, err := index.GenIndexKey(ctx.GetStochastikVars().StmtCtx, []types.Causet{types.NewCauset(ca.value)}, ekv.IntHandle(math.MinInt64), nil)
   223  		c.Assert(err, IsNil)
   224  		idx := searchLessEqualIdx(valueList, idxValue)
   225  		c.Assert(idx, Equals, ca.lessEqualIdx, Commentf("%#v", ca))
   226  
   227  		// Test for max int64 handle.
   228  		idxValue, _, err = index.GenIndexKey(ctx.GetStochastikVars().StmtCtx, []types.Causet{types.NewCauset(ca.value)}, ekv.IntHandle(math.MaxInt64), nil)
   229  		c.Assert(err, IsNil)
   230  		idx = searchLessEqualIdx(valueList, idxValue)
   231  		c.Assert(idx, Equals, ca.lessEqualIdx, Commentf("%#v", ca))
   232  	}
   233  
   234  	// Test for timestamp index.
   235  	// range is 2010-01-01 00:00:00 ~ 2020-01-01 00:00:00, and split into 10 region.
   236  	// So 10 regions range is like below:
   237  	// region1: [-inf					~ 2011-01-01 00:00:00)
   238  	// region2: [2011-01-01 00:00:00 	~ 2012-01-01 00:00:00)
   239  	// .
   240  	// .
   241  	// .
   242  	// region10: [2020-01-01 00:00:00 	~ +inf)
   243  	lowerTime := types.NewTime(types.FromDate(2010, 1, 1, 0, 0, 0, 0), allegrosql.TypeTimestamp, types.DefaultFsp)
   244  	upperTime := types.NewTime(types.FromDate(2020, 1, 1, 0, 0, 0, 0), allegrosql.TypeTimestamp, types.DefaultFsp)
   245  	e.lower = []types.Causet{types.NewCauset(lowerTime)}
   246  	e.upper = []types.Causet{types.NewCauset(upperTime)}
   247  	e.num = 10
   248  
   249  	// change index defCausumn type to timestamp
   250  	tbInfo.DeferredCausets[0].FieldType = *types.NewFieldType(allegrosql.TypeTimestamp)
   251  
   252  	valueList, err = e.getSplitIdxKeys()
   253  	sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 })
   254  	c.Assert(err, IsNil)
   255  	c.Assert(len(valueList), Equals, e.num+1)
   256  
   257  	cases3 := []struct {
   258  		value        types.CoreTime
   259  		lessEqualIdx int
   260  	}{
   261  		{types.FromDate(2009, 11, 20, 12, 50, 59, 0), 0},
   262  		{types.FromDate(2010, 1, 1, 0, 0, 0, 0), 0},
   263  		{types.FromDate(2011, 12, 31, 23, 59, 59, 0), 1},
   264  		{types.FromDate(2011, 2, 1, 0, 0, 0, 0), 1},
   265  		{types.FromDate(2012, 3, 1, 0, 0, 0, 0), 2},
   266  		{types.FromDate(2020, 4, 1, 0, 0, 0, 0), 3},
   267  		{types.FromDate(2020, 5, 1, 0, 0, 0, 0), 4},
   268  		{types.FromDate(2020, 6, 1, 0, 0, 0, 0), 5},
   269  		{types.FromDate(2020, 8, 1, 0, 0, 0, 0), 6},
   270  		{types.FromDate(2020, 9, 1, 0, 0, 0, 0), 7},
   271  		{types.FromDate(2020, 10, 1, 0, 0, 0, 0), 8},
   272  		{types.FromDate(2020, 11, 1, 0, 0, 0, 0), 9},
   273  		{types.FromDate(2020, 12, 1, 0, 0, 0, 0), 9},
   274  		{types.FromDate(2030, 12, 1, 0, 0, 0, 0), 9},
   275  	}
   276  
   277  	for _, ca := range cases3 {
   278  		value := types.NewTime(ca.value, allegrosql.TypeTimestamp, types.DefaultFsp)
   279  		// test for min int64 handle
   280  		idxValue, _, err := index.GenIndexKey(ctx.GetStochastikVars().StmtCtx, []types.Causet{types.NewCauset(value)}, ekv.IntHandle(math.MinInt64), nil)
   281  		c.Assert(err, IsNil)
   282  		idx := searchLessEqualIdx(valueList, idxValue)
   283  		c.Assert(idx, Equals, ca.lessEqualIdx, Commentf("%#v", ca))
   284  
   285  		// Test for max int64 handle.
   286  		idxValue, _, err = index.GenIndexKey(ctx.GetStochastikVars().StmtCtx, []types.Causet{types.NewCauset(value)}, ekv.IntHandle(math.MaxInt64), nil)
   287  		c.Assert(err, IsNil)
   288  		idx = searchLessEqualIdx(valueList, idxValue)
   289  		c.Assert(idx, Equals, ca.lessEqualIdx, Commentf("%#v", ca))
   290  	}
   291  }
   292  
   293  func (s *testSplitIndex) TestSplitBlock(c *C) {
   294  	tbInfo := &perceptron.BlockInfo{
   295  		Name: perceptron.NewCIStr("t1"),
   296  		ID:   rand.Int63(),
   297  		DeferredCausets: []*perceptron.DeferredCausetInfo{
   298  			{
   299  				Name:         perceptron.NewCIStr("c0"),
   300  				ID:           1,
   301  				Offset:       1,
   302  				DefaultValue: 0,
   303  				State:        perceptron.StatePublic,
   304  				FieldType:    *types.NewFieldType(allegrosql.TypeLong),
   305  			},
   306  		},
   307  	}
   308  	defer func(originValue int64) {
   309  		minRegionStepValue = originValue
   310  	}(minRegionStepValue)
   311  	minRegionStepValue = 10
   312  	// range is 0 ~ 100, and split into 10 region.
   313  	// So 10 regions range is like below:
   314  	// region1: [-inf ~ 10)
   315  	// region2: [10 ~ 20)
   316  	// region3: [20 ~ 30)
   317  	// region4: [30 ~ 40)
   318  	// region5: [40 ~ 50)
   319  	// region6: [50 ~ 60)
   320  	// region7: [60 ~ 70)
   321  	// region8: [70 ~ 80)
   322  	// region9: [80 ~ 90 )
   323  	// region10: [90 ~ +inf)
   324  	ctx := mock.NewContext()
   325  	e := &SplitBlockRegionInterDirc{
   326  		baseInterlockingDirectorate: newBaseInterlockingDirectorate(ctx, nil, 0),
   327  		blockInfo:                   tbInfo,
   328  		handleDefCauss:              embedded.NewIntHandleDefCauss(&memex.DeferredCauset{RetType: types.NewFieldType(allegrosql.TypeLonglong)}),
   329  		lower:                       []types.Causet{types.NewCauset(0)},
   330  		upper:                       []types.Causet{types.NewCauset(100)},
   331  		num:                         10,
   332  	}
   333  	valueList, err := e.getSplitBlockKeys()
   334  	c.Assert(err, IsNil)
   335  	c.Assert(len(valueList), Equals, e.num-1)
   336  
   337  	cases := []struct {
   338  		value        int
   339  		lessEqualIdx int
   340  	}{
   341  		{-1, -1},
   342  		{0, -1},
   343  		{1, -1},
   344  		{10, 0},
   345  		{11, 0},
   346  		{20, 1},
   347  		{21, 1},
   348  		{31, 2},
   349  		{41, 3},
   350  		{51, 4},
   351  		{61, 5},
   352  		{71, 6},
   353  		{81, 7},
   354  		{91, 8},
   355  		{100, 8},
   356  		{1000, 8},
   357  	}
   358  
   359  	recordPrefix := blockcodec.GenBlockRecordPrefix(e.blockInfo.ID)
   360  	for _, ca := range cases {
   361  		// test for minInt64 handle
   362  		key := blockcodec.EncodeRecordKey(recordPrefix, ekv.IntHandle(ca.value))
   363  		c.Assert(err, IsNil)
   364  		idx := searchLessEqualIdx(valueList, key)
   365  		c.Assert(idx, Equals, ca.lessEqualIdx, Commentf("%#v", ca))
   366  	}
   367  }
   368  
   369  func (s *testSplitIndex) TestClusterIndexSplitBlock(c *C) {
   370  	tbInfo := &perceptron.BlockInfo{
   371  		Name:           perceptron.NewCIStr("t"),
   372  		ID:             1,
   373  		IsCommonHandle: true,
   374  		Indices: []*perceptron.IndexInfo{
   375  			{
   376  				ID:      1,
   377  				Primary: true,
   378  				State:   perceptron.StatePublic,
   379  				DeferredCausets: []*perceptron.IndexDeferredCauset{
   380  					{Offset: 1},
   381  					{Offset: 2},
   382  				},
   383  			},
   384  		},
   385  		DeferredCausets: []*perceptron.DeferredCausetInfo{
   386  			{
   387  				Name:      perceptron.NewCIStr("c0"),
   388  				ID:        1,
   389  				Offset:    0,
   390  				State:     perceptron.StatePublic,
   391  				FieldType: *types.NewFieldType(allegrosql.TypeDouble),
   392  			},
   393  			{
   394  				Name:      perceptron.NewCIStr("c1"),
   395  				ID:        2,
   396  				Offset:    1,
   397  				State:     perceptron.StatePublic,
   398  				FieldType: *types.NewFieldType(allegrosql.TypeLonglong),
   399  			},
   400  			{
   401  				Name:      perceptron.NewCIStr("c2"),
   402  				ID:        3,
   403  				Offset:    2,
   404  				State:     perceptron.StatePublic,
   405  				FieldType: *types.NewFieldType(allegrosql.TypeLonglong),
   406  			},
   407  		},
   408  	}
   409  	defer func(originValue int64) {
   410  		minRegionStepValue = originValue
   411  	}(minRegionStepValue)
   412  	minRegionStepValue = 3
   413  	ctx := mock.NewContext()
   414  	sc := &stmtctx.StatementContext{TimeZone: time.Local}
   415  	e := &SplitBlockRegionInterDirc{
   416  		baseInterlockingDirectorate: newBaseInterlockingDirectorate(ctx, nil, 0),
   417  		blockInfo:                   tbInfo,
   418  		handleDefCauss:              buildHandleDefCaussForSplit(sc, tbInfo),
   419  		lower:                       types.MakeCausets(1, 0),
   420  		upper:                       types.MakeCausets(1, 100),
   421  		num:                         10,
   422  	}
   423  	valueList, err := e.getSplitBlockKeys()
   424  	c.Assert(err, IsNil)
   425  	c.Assert(len(valueList), Equals, e.num-1)
   426  
   427  	cases := []struct {
   428  		value        []types.Causet
   429  		lessEqualIdx int
   430  	}{
   431  		// For lower-bound and upper-bound, because 0 and 100 are padding with 7 zeros,
   432  		// the split points are not (i * 10) but approximation.
   433  		{types.MakeCausets(1, -1), -1},
   434  		{types.MakeCausets(1, 0), -1},
   435  		{types.MakeCausets(1, 10), -1},
   436  		{types.MakeCausets(1, 11), 0},
   437  		{types.MakeCausets(1, 20), 0},
   438  		{types.MakeCausets(1, 21), 1},
   439  
   440  		{types.MakeCausets(1, 31), 2},
   441  		{types.MakeCausets(1, 41), 3},
   442  		{types.MakeCausets(1, 51), 4},
   443  		{types.MakeCausets(1, 61), 5},
   444  		{types.MakeCausets(1, 71), 6},
   445  		{types.MakeCausets(1, 81), 7},
   446  		{types.MakeCausets(1, 91), 8},
   447  		{types.MakeCausets(1, 100), 8},
   448  		{types.MakeCausets(1, 101), 8},
   449  	}
   450  
   451  	recordPrefix := blockcodec.GenBlockRecordPrefix(e.blockInfo.ID)
   452  	for _, ca := range cases {
   453  		h, err := e.handleDefCauss.BuildHandleByCausets(ca.value)
   454  		c.Assert(err, IsNil)
   455  		key := blockcodec.EncodeRecordKey(recordPrefix, h)
   456  		c.Assert(err, IsNil)
   457  		idx := searchLessEqualIdx(valueList, key)
   458  		c.Assert(idx, Equals, ca.lessEqualIdx, Commentf("%#v", ca))
   459  	}
   460  }
   461  
   462  func searchLessEqualIdx(valueList [][]byte, value []byte) int {
   463  	idx := -1
   464  	for i, v := range valueList {
   465  		if bytes.Compare(value, v) >= 0 {
   466  			idx = i
   467  			continue
   468  		}
   469  		break
   470  	}
   471  	return idx
   472  }