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  }