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 }