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 }