github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/petri_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 petri
    15  
    16  import (
    17  	"context"
    18  	"crypto/tls"
    19  	"math"
    20  	"net"
    21  	"runtime"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/ngaut/pools"
    26  	dto "github.com/prometheus/client_perceptron/go"
    27  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    28  	"github.com/whtcorpsinc/BerolinaSQL/perceptron"
    29  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    30  	. "github.com/whtcorpsinc/check"
    31  	"github.com/whtcorpsinc/errors"
    32  	"github.com/whtcorpsinc/failpoint"
    33  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb/oracle"
    34  	"github.com/whtcorpsinc/milevadb/causetstore/mockstore"
    35  	"github.com/whtcorpsinc/milevadb/dbs"
    36  	"github.com/whtcorpsinc/milevadb/ekv"
    37  	"github.com/whtcorpsinc/milevadb/errno"
    38  	"github.com/whtcorpsinc/milevadb/metrics"
    39  	"github.com/whtcorpsinc/milevadb/petri/infosync"
    40  	"github.com/whtcorpsinc/milevadb/soliton"
    41  	"github.com/whtcorpsinc/milevadb/soliton/mock"
    42  	"github.com/whtcorpsinc/milevadb/soliton/testleak"
    43  	"github.com/whtcorpsinc/milevadb/spacetime"
    44  	"github.com/whtcorpsinc/milevadb/stochastikctx/variable"
    45  	"go.etcd.io/etcd/integration"
    46  )
    47  
    48  func TestT(t *testing.T) {
    49  	CustomVerboseFlag = true
    50  	TestingT(t)
    51  }
    52  
    53  var _ = Suite(&testSuite{})
    54  
    55  type testSuite struct {
    56  }
    57  
    58  func mockFactory() (pools.Resource, error) {
    59  	return nil, errors.New("mock factory should not be called")
    60  }
    61  
    62  func sysMockFactory(dom *Petri) (pools.Resource, error) {
    63  	return nil, nil
    64  }
    65  
    66  type mockEtcdBackend struct {
    67  	ekv.CausetStorage
    68  	FIDelAddrs []string
    69  }
    70  
    71  func (mebd *mockEtcdBackend) EtcdAddrs() ([]string, error) {
    72  	return mebd.FIDelAddrs, nil
    73  }
    74  func (mebd *mockEtcdBackend) TLSConfig() *tls.Config { return nil }
    75  func (mebd *mockEtcdBackend) StartGCWorker() error {
    76  	panic("not implemented")
    77  }
    78  
    79  // ETCD use ip:port as unix socket address, however this address is invalid on windows.
    80  // We have to skip some of the test in such case.
    81  // https://github.com/etcd-io/etcd/blob/f0faa5501d936cd8c9f561bb9d1baca70eb67ab1/pkg/types/urls.go#L42
    82  func unixSocketAvailable() bool {
    83  	c, err := net.Listen("unix", "127.0.0.1:0")
    84  	if err == nil {
    85  		c.Close()
    86  		return true
    87  	}
    88  	return false
    89  }
    90  
    91  func TestInfo(t *testing.T) {
    92  	if runtime.GOOS == "windows" {
    93  		t.Skip("integration.NewClusterV3 will create file contains a defCauson which is not allowed on Windows")
    94  	}
    95  	if !unixSocketAvailable() {
    96  		return
    97  	}
    98  	testleak.BeforeTest()
    99  	defer testleak.AfterTestT(t)()
   100  	dbsLease := 80 * time.Millisecond
   101  	s, err := mockstore.NewMockStore()
   102  	if err != nil {
   103  		t.Fatal(err)
   104  	}
   105  	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
   106  	defer clus.Terminate(t)
   107  	mockStore := &mockEtcdBackend{
   108  		CausetStorage: s,
   109  		FIDelAddrs:    []string{clus.Members[0].GRPCAddr()}}
   110  	dom := NewPetri(mockStore, dbsLease, 0, 0, mockFactory)
   111  	defer func() {
   112  		dom.Close()
   113  		s.Close()
   114  	}()
   115  
   116  	cli := clus.RandClient()
   117  	dom.etcdClient = cli
   118  	// Mock new DBS and init the schemaReplicant syncer with etcd client.
   119  	goCtx := context.Background()
   120  	dom.dbs = dbs.NewDBS(
   121  		goCtx,
   122  		dbs.WithEtcdClient(dom.GetEtcdClient()),
   123  		dbs.WithStore(s),
   124  		dbs.WithInfoHandle(dom.infoHandle),
   125  		dbs.WithLease(dbsLease),
   126  	)
   127  	err = dom.dbs.Start(nil)
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	err = failpoint.Enable("github.com/whtcorpsinc/milevadb/petri/MockReplaceDBS", `return(true)`)
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  	err = dom.Init(dbsLease, sysMockFactory)
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	err = failpoint.Disable("github.com/whtcorpsinc/milevadb/petri/MockReplaceDBS")
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  
   144  	// Test for GetServerInfo and GetServerInfoByID.
   145  	dbsID := dom.dbs.GetID()
   146  	serverInfo, err := infosync.GetServerInfo()
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  	info, err := infosync.GetServerInfoByID(goCtx, dbsID)
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  	if serverInfo.ID != info.ID {
   155  		t.Fatalf("server self info %v, info %v", serverInfo, info)
   156  	}
   157  	_, err = infosync.GetServerInfoByID(goCtx, "not_exist_id")
   158  	if err == nil || (err != nil && err.Error() != "[info-syncer] get /milevadb/server/info/not_exist_id failed") {
   159  		t.Fatal(err)
   160  	}
   161  
   162  	// Test for GetAllServerInfo.
   163  	infos, err := infosync.GetAllServerInfo(goCtx)
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  	if len(infos) != 1 || infos[dbsID].ID != info.ID {
   168  		t.Fatalf("server one info %v, info %v", infos[dbsID], info)
   169  	}
   170  
   171  	// Test the scene where syncer.Done() gets the information.
   172  	err = failpoint.Enable("github.com/whtcorpsinc/milevadb/dbs/soliton/ErrorMockStochastikDone", `return(true)`)
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  	<-dom.dbs.SchemaSyncer().Done()
   177  	err = failpoint.Disable("github.com/whtcorpsinc/milevadb/dbs/soliton/ErrorMockStochastikDone")
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  	time.Sleep(15 * time.Millisecond)
   182  	syncerStarted := false
   183  	for i := 0; i < 1000; i++ {
   184  		if dom.SchemaValidator.IsStarted() {
   185  			syncerStarted = true
   186  			break
   187  		}
   188  		time.Sleep(5 * time.Millisecond)
   189  	}
   190  	if !syncerStarted {
   191  		t.Fatal("start syncer failed")
   192  	}
   193  	// Make sure loading schemaReplicant is normal.
   194  	cs := &ast.CharsetOpt{
   195  		Chs:     "utf8",
   196  		DefCaus: "utf8_bin",
   197  	}
   198  	ctx := mock.NewContext()
   199  	err = dom.dbs.CreateSchema(ctx, perceptron.NewCIStr("aaa"), cs)
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	err = dom.Reload()
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  	if dom.SchemaReplicant().SchemaMetaVersion() != 1 {
   208  		t.Fatalf("uFIDelate schemaReplicant version failed, ver %d", dom.SchemaReplicant().SchemaMetaVersion())
   209  	}
   210  
   211  	// Test for RemoveServerInfo.
   212  	dom.info.RemoveServerInfo()
   213  	infos, err = infosync.GetAllServerInfo(goCtx)
   214  	if err != nil || len(infos) != 0 {
   215  		t.Fatalf("err %v, infos %v", err, infos)
   216  	}
   217  }
   218  
   219  type mockStochastikManager struct {
   220  	PS []*soliton.ProcessInfo
   221  }
   222  
   223  func (msm *mockStochastikManager) ShowProcessList() map[uint64]*soliton.ProcessInfo {
   224  	ret := make(map[uint64]*soliton.ProcessInfo)
   225  	for _, item := range msm.PS {
   226  		ret[item.ID] = item
   227  	}
   228  	return ret
   229  }
   230  
   231  func (msm *mockStochastikManager) GetProcessInfo(id uint64) (*soliton.ProcessInfo, bool) {
   232  	for _, item := range msm.PS {
   233  		if item.ID == id {
   234  			return item, true
   235  		}
   236  	}
   237  	return &soliton.ProcessInfo{}, false
   238  }
   239  
   240  func (msm *mockStochastikManager) Kill(cid uint64, query bool) {}
   241  
   242  func (msm *mockStochastikManager) UFIDelateTLSConfig(cfg *tls.Config) {}
   243  
   244  func (*testSuite) TestT(c *C) {
   245  	defer testleak.AfterTest(c)()
   246  	causetstore, err := mockstore.NewMockStore()
   247  	c.Assert(err, IsNil)
   248  	dbsLease := 80 * time.Millisecond
   249  	dom := NewPetri(causetstore, dbsLease, 0, 0, mockFactory)
   250  	err = dom.Init(dbsLease, sysMockFactory)
   251  	c.Assert(err, IsNil)
   252  	ctx := mock.NewContext()
   253  	ctx.CausetStore = dom.CausetStore()
   254  	dd := dom.DBS()
   255  	c.Assert(dd, NotNil)
   256  	c.Assert(dd.GetLease(), Equals, 80*time.Millisecond)
   257  
   258  	snapTS := oracle.EncodeTSO(oracle.GetPhysical(time.Now()))
   259  	cs := &ast.CharsetOpt{
   260  		Chs:     "utf8",
   261  		DefCaus: "utf8_bin",
   262  	}
   263  	err = dd.CreateSchema(ctx, perceptron.NewCIStr("aaa"), cs)
   264  	c.Assert(err, IsNil)
   265  	// Test for fetchSchemasWithBlocks when "blocks" isn't nil.
   266  	err = dd.CreateBlock(ctx, &ast.CreateBlockStmt{Block: &ast.BlockName{
   267  		Schema: perceptron.NewCIStr("aaa"),
   268  		Name:   perceptron.NewCIStr("tbl")}})
   269  	c.Assert(err, IsNil)
   270  	is := dom.SchemaReplicant()
   271  	c.Assert(is, NotNil)
   272  
   273  	// for uFIDelating the self schemaReplicant version
   274  	goCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   275  	err = dd.SchemaSyncer().TenantCheckAllVersions(goCtx, is.SchemaMetaVersion())
   276  	cancel()
   277  	c.Assert(err, IsNil)
   278  	snapIs, err := dom.GetSnapshotSchemaReplicant(snapTS)
   279  	c.Assert(snapIs, NotNil)
   280  	c.Assert(err, IsNil)
   281  	// Make sure that the self schemaReplicant version doesn't be changed.
   282  	goCtx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond)
   283  	err = dd.SchemaSyncer().TenantCheckAllVersions(goCtx, is.SchemaMetaVersion())
   284  	cancel()
   285  	c.Assert(err, IsNil)
   286  
   287  	// for GetSnapshotSchemaReplicant
   288  	currSnapTS := oracle.EncodeTSO(oracle.GetPhysical(time.Now()))
   289  	currSnapIs, err := dom.GetSnapshotSchemaReplicant(currSnapTS)
   290  	c.Assert(err, IsNil)
   291  	c.Assert(currSnapIs, NotNil)
   292  	c.Assert(currSnapIs.SchemaMetaVersion(), Equals, is.SchemaMetaVersion())
   293  
   294  	// for GetSnapshotMeta
   295  	dbInfo, ok := currSnapIs.SchemaByName(perceptron.NewCIStr("aaa"))
   296  	c.Assert(ok, IsTrue)
   297  	tbl, err := currSnapIs.BlockByName(perceptron.NewCIStr("aaa"), perceptron.NewCIStr("tbl"))
   298  	c.Assert(err, IsNil)
   299  	m, err := dom.GetSnapshotMeta(snapTS)
   300  	c.Assert(err, IsNil)
   301  	tblInfo1, err := m.GetBlock(dbInfo.ID, tbl.Meta().ID)
   302  	c.Assert(spacetime.ErrDBNotExists.Equal(err), IsTrue)
   303  	c.Assert(tblInfo1, IsNil)
   304  	m, err = dom.GetSnapshotMeta(currSnapTS)
   305  	c.Assert(err, IsNil)
   306  	tblInfo2, err := m.GetBlock(dbInfo.ID, tbl.Meta().ID)
   307  	c.Assert(err, IsNil)
   308  	c.Assert(tbl.Meta(), DeepEquals, tblInfo2)
   309  
   310  	// Test for tryLoadSchemaDiffs when "isTooOldSchema" is false.
   311  	err = dd.CreateSchema(ctx, perceptron.NewCIStr("bbb"), cs)
   312  	c.Assert(err, IsNil)
   313  	err = dom.Reload()
   314  	c.Assert(err, IsNil)
   315  
   316  	// for schemaValidator
   317  	schemaVer := dom.SchemaValidator.(*schemaValidator).LatestSchemaVersion()
   318  	ver, err := causetstore.CurrentVersion()
   319  	c.Assert(err, IsNil)
   320  	ts := ver.Ver
   321  
   322  	_, succ := dom.SchemaValidator.Check(ts, schemaVer, nil)
   323  	c.Assert(succ, Equals, ResultSucc)
   324  	c.Assert(failpoint.Enable("github.com/whtcorpsinc/milevadb/petri/ErrorMockReloadFailed", `return(true)`), IsNil)
   325  	err = dom.Reload()
   326  	c.Assert(err, NotNil)
   327  	_, succ = dom.SchemaValidator.Check(ts, schemaVer, nil)
   328  	c.Assert(succ, Equals, ResultSucc)
   329  	time.Sleep(dbsLease)
   330  
   331  	ver, err = causetstore.CurrentVersion()
   332  	c.Assert(err, IsNil)
   333  	ts = ver.Ver
   334  	_, succ = dom.SchemaValidator.Check(ts, schemaVer, nil)
   335  	c.Assert(succ, Equals, ResultUnknown)
   336  	c.Assert(failpoint.Disable("github.com/whtcorpsinc/milevadb/petri/ErrorMockReloadFailed"), IsNil)
   337  	err = dom.Reload()
   338  	c.Assert(err, IsNil)
   339  	_, succ = dom.SchemaValidator.Check(ts, schemaVer, nil)
   340  	c.Assert(succ, Equals, ResultSucc)
   341  
   342  	// For slow query.
   343  	dom.LogSlowQuery(&SlowQueryInfo{ALLEGROALLEGROSQL: "aaa", Duration: time.Second, Internal: true})
   344  	dom.LogSlowQuery(&SlowQueryInfo{ALLEGROALLEGROSQL: "bbb", Duration: 3 * time.Second})
   345  	dom.LogSlowQuery(&SlowQueryInfo{ALLEGROALLEGROSQL: "ccc", Duration: 2 * time.Second})
   346  	// DefCauslecting slow queries is asynchronous, wait a while to ensure it's done.
   347  	time.Sleep(5 * time.Millisecond)
   348  
   349  	res := dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 2})
   350  	c.Assert(res, HasLen, 2)
   351  	c.Assert(res[0].ALLEGROALLEGROSQL, Equals, "bbb")
   352  	c.Assert(res[0].Duration, Equals, 3*time.Second)
   353  	c.Assert(res[1].ALLEGROALLEGROSQL, Equals, "ccc")
   354  	c.Assert(res[1].Duration, Equals, 2*time.Second)
   355  
   356  	res = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 2, HoTT: ast.ShowSlowHoTTInternal})
   357  	c.Assert(res, HasLen, 1)
   358  	c.Assert(res[0].ALLEGROALLEGROSQL, Equals, "aaa")
   359  	c.Assert(res[0].Duration, Equals, time.Second)
   360  	c.Assert(res[0].Internal, Equals, true)
   361  
   362  	res = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 4, HoTT: ast.ShowSlowHoTTAll})
   363  	c.Assert(res, HasLen, 3)
   364  	c.Assert(res[0].ALLEGROALLEGROSQL, Equals, "bbb")
   365  	c.Assert(res[0].Duration, Equals, 3*time.Second)
   366  	c.Assert(res[1].ALLEGROALLEGROSQL, Equals, "ccc")
   367  	c.Assert(res[1].Duration, Equals, 2*time.Second)
   368  	c.Assert(res[2].ALLEGROALLEGROSQL, Equals, "aaa")
   369  	c.Assert(res[2].Duration, Equals, time.Second)
   370  	c.Assert(res[2].Internal, Equals, true)
   371  
   372  	res = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowRecent, Count: 2})
   373  	c.Assert(res, HasLen, 2)
   374  	c.Assert(res[0].ALLEGROALLEGROSQL, Equals, "ccc")
   375  	c.Assert(res[0].Duration, Equals, 2*time.Second)
   376  	c.Assert(res[1].ALLEGROALLEGROSQL, Equals, "bbb")
   377  	c.Assert(res[1].Duration, Equals, 3*time.Second)
   378  
   379  	metrics.PanicCounter.Reset()
   380  	// Since the stats lease is 0 now, so create a new ticker will panic.
   381  	// Test that they can recover from panic correctly.
   382  	dom.uFIDelateStatsWorker(ctx, nil)
   383  	dom.autoAnalyzeWorker(nil)
   384  	counter := metrics.PanicCounter.WithLabelValues(metrics.LabelPetri)
   385  	pb := &dto.Metric{}
   386  	counter.Write(pb)
   387  	c.Assert(pb.GetCounter().GetValue(), Equals, float64(2))
   388  
   389  	scope := dom.GetScope("status")
   390  	c.Assert(scope, Equals, variable.DefaultStatusVarScopeFlag)
   391  
   392  	// For schemaReplicant check, it tests for getting the result of "ResultUnknown".
   393  	schemaChecker := NewSchemaChecker(dom, is.SchemaMetaVersion(), nil)
   394  	originalRetryTime := SchemaOutOfDateRetryTimes
   395  	originalRetryInterval := SchemaOutOfDateRetryInterval
   396  	// Make sure it will retry one time and doesn't take a long time.
   397  	SchemaOutOfDateRetryTimes = 1
   398  	SchemaOutOfDateRetryInterval = int64(time.Millisecond * 1)
   399  	defer func() {
   400  		SchemaOutOfDateRetryTimes = originalRetryTime
   401  		SchemaOutOfDateRetryInterval = originalRetryInterval
   402  	}()
   403  	dom.SchemaValidator.Stop()
   404  	_, err = schemaChecker.Check(uint64(123456))
   405  	c.Assert(err.Error(), Equals, ErrSchemaReplicantExpired.Error())
   406  	dom.SchemaValidator.Reset()
   407  
   408  	// Test for reporting min start timestamp.
   409  	infoSyncer := dom.InfoSyncer()
   410  	sm := &mockStochastikManager{
   411  		PS: make([]*soliton.ProcessInfo, 0),
   412  	}
   413  	infoSyncer.SetStochastikManager(sm)
   414  	beforeTS := variable.GoTimeToTS(time.Now())
   415  	infoSyncer.ReportMinStartTS(dom.CausetStore())
   416  	afterTS := variable.GoTimeToTS(time.Now())
   417  	c.Assert(infoSyncer.GetMinStartTS() > beforeTS && infoSyncer.GetMinStartTS() < afterTS, IsFalse)
   418  	lowerLimit := time.Now().Add(-time.Duration(ekv.MaxTxnTimeUse) * time.Millisecond)
   419  	validTS := variable.GoTimeToTS(lowerLimit.Add(time.Minute))
   420  	sm.PS = []*soliton.ProcessInfo{
   421  		{CurTxnStartTS: 0},
   422  		{CurTxnStartTS: math.MaxUint64},
   423  		{CurTxnStartTS: variable.GoTimeToTS(lowerLimit)},
   424  		{CurTxnStartTS: validTS},
   425  	}
   426  	infoSyncer.SetStochastikManager(sm)
   427  	infoSyncer.ReportMinStartTS(dom.CausetStore())
   428  	c.Assert(infoSyncer.GetMinStartTS() == validTS, IsTrue)
   429  
   430  	err = causetstore.Close()
   431  	c.Assert(err, IsNil)
   432  	isClose := dom.isClose()
   433  	c.Assert(isClose, IsFalse)
   434  	dom.Close()
   435  	isClose = dom.isClose()
   436  	c.Assert(isClose, IsTrue)
   437  }
   438  
   439  type testResource struct {
   440  	status int
   441  }
   442  
   443  func (tr *testResource) Close() { tr.status = 1 }
   444  
   445  func (*testSuite) TestStochastikPool(c *C) {
   446  	f := func() (pools.Resource, error) { return &testResource{}, nil }
   447  	pool := newStochastikPool(1, f)
   448  	tr, err := pool.Get()
   449  	c.Assert(err, IsNil)
   450  	tr1, err := pool.Get()
   451  	c.Assert(err, IsNil)
   452  	pool.Put(tr)
   453  	// Capacity is 1, so tr1 is closed.
   454  	pool.Put(tr1)
   455  	c.Assert(tr1.(*testResource).status, Equals, 1)
   456  	pool.Close()
   457  
   458  	pool.Close()
   459  	pool.Put(tr1)
   460  	tr, err = pool.Get()
   461  	c.Assert(err.Error(), Equals, "stochastik pool closed")
   462  	c.Assert(tr, IsNil)
   463  }
   464  
   465  func (*testSuite) TestErrorCode(c *C) {
   466  	c.Assert(int(terror.ToALLEGROSQLError(ErrSchemaReplicantExpired).Code), Equals, errno.ErrSchemaReplicantExpired)
   467  	c.Assert(int(terror.ToALLEGROSQLError(ErrSchemaReplicantChanged).Code), Equals, errno.ErrSchemaReplicantChanged)
   468  }