github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/dbs/defcaus_change_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 dbs 15 16 import ( 17 "context" 18 "fmt" 19 "sync" 20 "sync/atomic" 21 "time" 22 23 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 24 "github.com/whtcorpsinc/BerolinaSQL/ast" 25 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 26 . "github.com/whtcorpsinc/check" 27 "github.com/whtcorpsinc/errors" 28 "github.com/whtcorpsinc/milevadb/causet" 29 "github.com/whtcorpsinc/milevadb/ekv" 30 "github.com/whtcorpsinc/milevadb/soliton/mock" 31 "github.com/whtcorpsinc/milevadb/soliton/solitonutil" 32 "github.com/whtcorpsinc/milevadb/spacetime" 33 "github.com/whtcorpsinc/milevadb/spacetime/autoid" 34 "github.com/whtcorpsinc/milevadb/stochastikctx" 35 "github.com/whtcorpsinc/milevadb/types" 36 ) 37 38 var _ = Suite(&testDeferredCausetChangeSuite{}) 39 40 type testDeferredCausetChangeSuite struct { 41 causetstore ekv.CausetStorage 42 dbInfo *perceptron.DBInfo 43 } 44 45 func (s *testDeferredCausetChangeSuite) SetUpSuite(c *C) { 46 SetWaitTimeWhenErrorOccurred(1 * time.Microsecond) 47 s.causetstore = testCreateStore(c, "test_defCausumn_change") 48 s.dbInfo = &perceptron.DBInfo{ 49 Name: perceptron.NewCIStr("test_defCausumn_change"), 50 ID: 1, 51 } 52 err := ekv.RunInNewTxn(s.causetstore, true, func(txn ekv.Transaction) error { 53 t := spacetime.NewMeta(txn) 54 return errors.Trace(t.CreateDatabase(s.dbInfo)) 55 }) 56 c.Check(err, IsNil) 57 } 58 59 func (s *testDeferredCausetChangeSuite) TearDownSuite(c *C) { 60 s.causetstore.Close() 61 } 62 63 func (s *testDeferredCausetChangeSuite) TestDeferredCausetChange(c *C) { 64 d := testNewDBSAndStart( 65 context.Background(), 66 c, 67 WithStore(s.causetstore), 68 WithLease(testLease), 69 ) 70 defer d.Stop() 71 // create causet t (c1 int, c2 int); 72 tblInfo := testBlockInfo(c, d, "t", 2) 73 ctx := testNewContext(d) 74 err := ctx.NewTxn(context.Background()) 75 c.Assert(err, IsNil) 76 testCreateBlock(c, ctx, d, s.dbInfo, tblInfo) 77 // insert t values (1, 2); 78 originBlock := testGetBlock(c, d, s.dbInfo.ID, tblInfo.ID) 79 event := types.MakeCausets(1, 2) 80 h, err := originBlock.AddRecord(ctx, event) 81 c.Assert(err, IsNil) 82 txn, err := ctx.Txn(true) 83 c.Assert(err, IsNil) 84 err = txn.Commit(context.Background()) 85 c.Assert(err, IsNil) 86 87 var mu sync.Mutex 88 tc := &TestDBSCallback{} 89 // set up hook 90 prevState := perceptron.StateNone 91 var ( 92 deleteOnlyBlock causet.Block 93 writeOnlyBlock causet.Block 94 publicBlock causet.Block 95 ) 96 var checkErr error 97 tc.onJobUFIDelated = func(job *perceptron.Job) { 98 if job.SchemaState == prevState { 99 return 100 } 101 hookCtx := mock.NewContext() 102 hookCtx.CausetStore = s.causetstore 103 prevState = job.SchemaState 104 err := hookCtx.NewTxn(context.Background()) 105 if err != nil { 106 checkErr = errors.Trace(err) 107 } 108 switch job.SchemaState { 109 case perceptron.StateDeleteOnly: 110 deleteOnlyBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID) 111 if err != nil { 112 checkErr = errors.Trace(err) 113 } 114 case perceptron.StateWriteOnly: 115 writeOnlyBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID) 116 if err != nil { 117 checkErr = errors.Trace(err) 118 } 119 err = s.checkAddWriteOnly(hookCtx, d, deleteOnlyBlock, writeOnlyBlock, h) 120 if err != nil { 121 checkErr = errors.Trace(err) 122 } 123 case perceptron.StatePublic: 124 mu.Lock() 125 publicBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID) 126 if err != nil { 127 checkErr = errors.Trace(err) 128 } 129 err = s.checkAddPublic(hookCtx, d, writeOnlyBlock, publicBlock) 130 if err != nil { 131 checkErr = errors.Trace(err) 132 } 133 mu.Unlock() 134 } 135 txn, err := hookCtx.Txn(true) 136 if err != nil { 137 checkErr = errors.Trace(err) 138 } 139 err = txn.Commit(context.Background()) 140 if err != nil { 141 checkErr = errors.Trace(err) 142 } 143 } 144 d.SetHook(tc) 145 defaultValue := int64(3) 146 job := testCreateDeferredCauset(c, ctx, d, s.dbInfo, tblInfo, "c3", &ast.DeferredCausetPosition{Tp: ast.DeferredCausetPositionNone}, defaultValue) 147 c.Assert(errors.ErrorStack(checkErr), Equals, "") 148 testCheckJobDone(c, d, job, true) 149 mu.Lock() 150 tb := publicBlock 151 mu.Unlock() 152 s.testDeferredCausetDrop(c, ctx, d, tb) 153 s.testAddDeferredCausetNoDefault(c, ctx, d, tblInfo) 154 } 155 156 func (s *testDeferredCausetChangeSuite) TestModifyAutoRandDeferredCausetWithMetaKeyChanged(c *C) { 157 d := testNewDBSAndStart( 158 context.Background(), 159 c, 160 WithStore(s.causetstore), 161 WithLease(testLease), 162 ) 163 defer d.Stop() 164 165 ids, err := d.genGlobalIDs(1) 166 blockID := ids[0] 167 c.Assert(err, IsNil) 168 defCausInfo := &perceptron.DeferredCausetInfo{ 169 Name: perceptron.NewCIStr("a"), 170 Offset: 0, 171 State: perceptron.StatePublic, 172 FieldType: *types.NewFieldType(allegrosql.TypeLonglong), 173 } 174 tblInfo := &perceptron.BlockInfo{ 175 ID: blockID, 176 Name: perceptron.NewCIStr("auto_random_block_name"), 177 DeferredCausets: []*perceptron.DeferredCausetInfo{defCausInfo}, 178 AutoRandomBits: 5, 179 } 180 defCausInfo.ID = allocateDeferredCausetID(tblInfo) 181 ctx := testNewContext(d) 182 testCreateBlock(c, ctx, d, s.dbInfo, tblInfo) 183 184 tc := &TestDBSCallback{} 185 var errCount int32 = 3 186 var genAutoRandErr error 187 tc.onJobRunBefore = func(job *perceptron.Job) { 188 if atomic.LoadInt32(&errCount) > 0 && job.Type == perceptron.CausetActionModifyDeferredCauset { 189 atomic.AddInt32(&errCount, -1) 190 genAutoRandErr = ekv.RunInNewTxn(s.causetstore, false, func(txn ekv.Transaction) error { 191 t := spacetime.NewMeta(txn) 192 _, err1 := t.GenAutoRandomID(s.dbInfo.ID, blockID, 1) 193 return err1 194 }) 195 } 196 } 197 d.SetHook(tc) 198 const newAutoRandomBits uint64 = 10 199 job := &perceptron.Job{ 200 SchemaID: s.dbInfo.ID, 201 BlockID: tblInfo.ID, 202 SchemaName: s.dbInfo.Name.L, 203 Type: perceptron.CausetActionModifyDeferredCauset, 204 BinlogInfo: &perceptron.HistoryInfo{}, 205 Args: []interface{}{defCausInfo, defCausInfo.Name, ast.DeferredCausetPosition{}, 0, newAutoRandomBits}, 206 } 207 err = d.doDBSJob(ctx, job) 208 c.Assert(err, IsNil) 209 c.Assert(errCount == 0, IsTrue) 210 c.Assert(genAutoRandErr, IsNil) 211 testCheckJobDone(c, d, job, true) 212 var newTbInfo *perceptron.BlockInfo 213 err = ekv.RunInNewTxn(d.causetstore, false, func(txn ekv.Transaction) error { 214 t := spacetime.NewMeta(txn) 215 var err error 216 newTbInfo, err = t.GetBlock(s.dbInfo.ID, blockID) 217 if err != nil { 218 return errors.Trace(err) 219 } 220 return nil 221 }) 222 c.Assert(err, IsNil) 223 c.Assert(newTbInfo.AutoRandomBits, Equals, newAutoRandomBits) 224 } 225 226 func (s *testDeferredCausetChangeSuite) testAddDeferredCausetNoDefault(c *C, ctx stochastikctx.Context, d *dbs, tblInfo *perceptron.BlockInfo) { 227 tc := &TestDBSCallback{} 228 // set up hook 229 prevState := perceptron.StateNone 230 var checkErr error 231 var writeOnlyBlock causet.Block 232 tc.onJobUFIDelated = func(job *perceptron.Job) { 233 if job.SchemaState == prevState { 234 return 235 } 236 hookCtx := mock.NewContext() 237 hookCtx.CausetStore = s.causetstore 238 prevState = job.SchemaState 239 err := hookCtx.NewTxn(context.Background()) 240 if err != nil { 241 checkErr = errors.Trace(err) 242 } 243 switch job.SchemaState { 244 case perceptron.StateWriteOnly: 245 writeOnlyBlock, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID) 246 if err != nil { 247 checkErr = errors.Trace(err) 248 } 249 case perceptron.StatePublic: 250 _, err = getCurrentBlock(d, s.dbInfo.ID, tblInfo.ID) 251 if err != nil { 252 checkErr = errors.Trace(err) 253 } 254 _, err = writeOnlyBlock.AddRecord(hookCtx, types.MakeCausets(10, 10)) 255 if err != nil { 256 checkErr = errors.Trace(err) 257 } 258 } 259 txn, err := hookCtx.Txn(true) 260 if err != nil { 261 checkErr = errors.Trace(err) 262 } 263 err = txn.Commit(context.TODO()) 264 if err != nil { 265 checkErr = errors.Trace(err) 266 } 267 } 268 d.SetHook(tc) 269 job := testCreateDeferredCauset(c, ctx, d, s.dbInfo, tblInfo, "c3", &ast.DeferredCausetPosition{Tp: ast.DeferredCausetPositionNone}, nil) 270 c.Assert(errors.ErrorStack(checkErr), Equals, "") 271 testCheckJobDone(c, d, job, true) 272 } 273 274 func (s *testDeferredCausetChangeSuite) testDeferredCausetDrop(c *C, ctx stochastikctx.Context, d *dbs, tbl causet.Block) { 275 dropDefCaus := tbl.DefCauss()[2] 276 tc := &TestDBSCallback{} 277 // set up hook 278 prevState := perceptron.StateNone 279 var checkErr error 280 tc.onJobUFIDelated = func(job *perceptron.Job) { 281 if job.SchemaState == prevState { 282 return 283 } 284 prevState = job.SchemaState 285 currentTbl, err := getCurrentBlock(d, s.dbInfo.ID, tbl.Meta().ID) 286 if err != nil { 287 checkErr = errors.Trace(err) 288 } 289 for _, defCaus := range currentTbl.DefCauss() { 290 if defCaus.ID == dropDefCaus.ID { 291 checkErr = errors.Errorf("defCausumn is not dropped") 292 } 293 } 294 } 295 d.SetHook(tc) 296 c.Assert(errors.ErrorStack(checkErr), Equals, "") 297 testDropDeferredCauset(c, ctx, d, s.dbInfo, tbl.Meta(), dropDefCaus.Name.L, false) 298 } 299 300 func (s *testDeferredCausetChangeSuite) checkAddWriteOnly(ctx stochastikctx.Context, d *dbs, deleteOnlyBlock, writeOnlyBlock causet.Block, h ekv.Handle) error { 301 // WriteOnlyBlock: insert t values (2, 3) 302 err := ctx.NewTxn(context.Background()) 303 if err != nil { 304 return errors.Trace(err) 305 } 306 _, err = writeOnlyBlock.AddRecord(ctx, types.MakeCausets(2, 3)) 307 if err != nil { 308 return errors.Trace(err) 309 } 310 err = ctx.NewTxn(context.Background()) 311 if err != nil { 312 return errors.Trace(err) 313 } 314 err = checkResult(ctx, writeOnlyBlock, writeOnlyBlock.WriblockDefCauss(), 315 solitonutil.RowsWithSep(" ", "1 2 <nil>", "2 3 3")) 316 if err != nil { 317 return errors.Trace(err) 318 } 319 // This test is for RowWithDefCauss when defCausumn state is StateWriteOnly. 320 event, err := writeOnlyBlock.RowWithDefCauss(ctx, h, writeOnlyBlock.WriblockDefCauss()) 321 if err != nil { 322 return errors.Trace(err) 323 } 324 got := fmt.Sprintf("%v", event) 325 expect := fmt.Sprintf("%v", []types.Causet{types.NewCauset(1), types.NewCauset(2), types.NewCauset(nil)}) 326 if got != expect { 327 return errors.Errorf("expect %v, got %v", expect, got) 328 } 329 // DeleteOnlyBlock: select * from t 330 err = checkResult(ctx, deleteOnlyBlock, deleteOnlyBlock.WriblockDefCauss(), solitonutil.RowsWithSep(" ", "1 2", "2 3")) 331 if err != nil { 332 return errors.Trace(err) 333 } 334 // WriteOnlyBlock: uFIDelate t set c1 = 2 where c1 = 1 335 h, _, err = writeOnlyBlock.Seek(ctx, ekv.IntHandle(0)) 336 if err != nil { 337 return errors.Trace(err) 338 } 339 err = writeOnlyBlock.UFIDelateRecord(context.Background(), ctx, h, types.MakeCausets(1, 2, 3), types.MakeCausets(2, 2, 3), touchedSlice(writeOnlyBlock)) 340 if err != nil { 341 return errors.Trace(err) 342 } 343 err = ctx.NewTxn(context.Background()) 344 if err != nil { 345 return errors.Trace(err) 346 } 347 // After we uFIDelate the first event, its default value is also set. 348 err = checkResult(ctx, writeOnlyBlock, writeOnlyBlock.WriblockDefCauss(), solitonutil.RowsWithSep(" ", "2 2 3", "2 3 3")) 349 if err != nil { 350 return errors.Trace(err) 351 } 352 // DeleteOnlyBlock: delete from t where c2 = 2 353 err = deleteOnlyBlock.RemoveRecord(ctx, h, types.MakeCausets(2, 2)) 354 if err != nil { 355 return errors.Trace(err) 356 } 357 err = ctx.NewTxn(context.Background()) 358 if err != nil { 359 return errors.Trace(err) 360 } 361 // After delete causet has deleted the first event, check the WriteOnly causet records. 362 err = checkResult(ctx, writeOnlyBlock, writeOnlyBlock.WriblockDefCauss(), solitonutil.RowsWithSep(" ", "2 3 3")) 363 return errors.Trace(err) 364 } 365 366 func touchedSlice(t causet.Block) []bool { 367 touched := make([]bool, 0, len(t.WriblockDefCauss())) 368 for range t.WriblockDefCauss() { 369 touched = append(touched, true) 370 } 371 return touched 372 } 373 374 func (s *testDeferredCausetChangeSuite) checkAddPublic(sctx stochastikctx.Context, d *dbs, writeOnlyBlock, publicBlock causet.Block) error { 375 ctx := context.TODO() 376 // publicBlock Insert t values (4, 4, 4) 377 err := sctx.NewTxn(ctx) 378 if err != nil { 379 return errors.Trace(err) 380 } 381 h, err := publicBlock.AddRecord(sctx, types.MakeCausets(4, 4, 4)) 382 if err != nil { 383 return errors.Trace(err) 384 } 385 err = sctx.NewTxn(ctx) 386 if err != nil { 387 return errors.Trace(err) 388 } 389 // writeOnlyBlock uFIDelate t set c1 = 3 where c1 = 4 390 oldRow, err := writeOnlyBlock.RowWithDefCauss(sctx, h, writeOnlyBlock.WriblockDefCauss()) 391 if err != nil { 392 return errors.Trace(err) 393 } 394 if len(oldRow) != 3 { 395 return errors.Errorf("%v", oldRow) 396 } 397 newRow := types.MakeCausets(3, 4, oldRow[2].GetValue()) 398 err = writeOnlyBlock.UFIDelateRecord(context.Background(), sctx, h, oldRow, newRow, touchedSlice(writeOnlyBlock)) 399 if err != nil { 400 return errors.Trace(err) 401 } 402 err = sctx.NewTxn(ctx) 403 if err != nil { 404 return errors.Trace(err) 405 } 406 // publicBlock select * from t, make sure the new c3 value 4 is not overwritten to default value 3. 407 err = checkResult(sctx, publicBlock, publicBlock.WriblockDefCauss(), solitonutil.RowsWithSep(" ", "2 3 3", "3 4 4")) 408 if err != nil { 409 return errors.Trace(err) 410 } 411 return nil 412 } 413 414 func getCurrentBlock(d *dbs, schemaID, blockID int64) (causet.Block, error) { 415 var tblInfo *perceptron.BlockInfo 416 err := ekv.RunInNewTxn(d.causetstore, false, func(txn ekv.Transaction) error { 417 t := spacetime.NewMeta(txn) 418 var err error 419 tblInfo, err = t.GetBlock(schemaID, blockID) 420 if err != nil { 421 return errors.Trace(err) 422 } 423 return nil 424 }) 425 if err != nil { 426 return nil, errors.Trace(err) 427 } 428 alloc := autoid.NewSlabPredictor(d.causetstore, schemaID, false, autoid.RowIDAllocType) 429 tbl, err := causet.BlockFromMeta(autoid.NewSlabPredictors(alloc), tblInfo) 430 if err != nil { 431 return nil, errors.Trace(err) 432 } 433 return tbl, err 434 } 435 436 func checkResult(ctx stochastikctx.Context, t causet.Block, defcaus []*causet.DeferredCauset, rows [][]interface{}) error { 437 var gotRows [][]interface{} 438 err := t.IterRecords(ctx, t.FirstKey(), defcaus, func(_ ekv.Handle, data []types.Causet, defcaus []*causet.DeferredCauset) (bool, error) { 439 gotRows = append(gotRows, datumsToInterfaces(data)) 440 return true, nil 441 }) 442 if err != nil { 443 return err 444 } 445 got := fmt.Sprintf("%v", gotRows) 446 expect := fmt.Sprintf("%v", rows) 447 if got != expect { 448 return errors.Errorf("expect %v, got %v", expect, got) 449 } 450 return nil 451 } 452 453 func datumsToInterfaces(datums []types.Causet) []interface{} { 454 ifs := make([]interface{}, 0, len(datums)) 455 for _, d := range datums { 456 ifs = append(ifs, d.GetValue()) 457 } 458 return ifs 459 }