github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/dbs/dagger/soliton/syncer_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 soliton_test
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"runtime"
    20  	"sync"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    25  	. "github.com/whtcorpsinc/check"
    26  	"github.com/whtcorpsinc/errors"
    27  	"github.com/whtcorpsinc/milevadb/causetstore/mockstore"
    28  	. "github.com/whtcorpsinc/milevadb/dbs"
    29  	. "github.com/whtcorpsinc/milevadb/dbs/soliton"
    30  	"github.com/whtcorpsinc/milevadb/tenant"
    31  	"go.etcd.io/etcd/clientv3"
    32  	"go.etcd.io/etcd/etcdserver"
    33  	"go.etcd.io/etcd/integration"
    34  	"go.etcd.io/etcd/mvcc/mvccpb"
    35  	goctx "golang.org/x/net/context"
    36  	"google.golang.org/grpc/codes"
    37  	"google.golang.org/grpc/status"
    38  )
    39  
    40  func TestT(t *testing.T) {
    41  	TestingT(t)
    42  }
    43  
    44  const minInterval = 10 * time.Nanosecond // It's used to test timeout.
    45  
    46  func TestSyncerSimple(t *testing.T) {
    47  	if runtime.GOOS == "windows" {
    48  		t.Skip("integration.NewClusterV3 will create file contains a defCauson which is not allowed on Windows")
    49  	}
    50  	testLease := 5 * time.Millisecond
    51  	origin := CheckVersFirstWaitTime
    52  	CheckVersFirstWaitTime = 0
    53  	defer func() {
    54  		CheckVersFirstWaitTime = origin
    55  	}()
    56  
    57  	causetstore, err := mockstore.NewMockStore()
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  	defer causetstore.Close()
    62  
    63  	clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
    64  	defer clus.Terminate(t)
    65  	cli := clus.RandClient()
    66  	ctx := goctx.Background()
    67  	d := NewDBS(
    68  		ctx,
    69  		WithEtcdClient(cli),
    70  		WithStore(causetstore),
    71  		WithLease(testLease),
    72  	)
    73  	err = d.Start(nil)
    74  	if err != nil {
    75  		t.Fatalf("DBS start failed %v", err)
    76  	}
    77  	defer d.Stop()
    78  
    79  	// for init function
    80  	if err = d.SchemaSyncer().Init(ctx); err != nil {
    81  		t.Fatalf("schemaReplicant version syncer init failed %v", err)
    82  	}
    83  	resp, err := cli.Get(ctx, DBSAllSchemaVersions, clientv3.WithPrefix())
    84  	if err != nil {
    85  		t.Fatalf("client get version failed %v", err)
    86  	}
    87  	key := DBSAllSchemaVersions + "/" + d.TenantManager().ID()
    88  	checkRespKV(t, 1, key, InitialVersion, resp.Ekvs...)
    89  	// for MustGetGlobalVersion function
    90  	globalVer, err := d.SchemaSyncer().MustGetGlobalVersion(ctx)
    91  	if err != nil {
    92  		t.Fatalf("client get global version failed %v", err)
    93  	}
    94  	if InitialVersion != fmt.Sprintf("%d", globalVer) {
    95  		t.Fatalf("client get global version %d isn't equal to init version %s", globalVer, InitialVersion)
    96  	}
    97  	childCtx, _ := goctx.WithTimeout(ctx, minInterval)
    98  	_, err = d.SchemaSyncer().MustGetGlobalVersion(childCtx)
    99  	if !isTimeoutError(err) {
   100  		t.Fatalf("client get global version result not match, err %v", err)
   101  	}
   102  
   103  	d1 := NewDBS(
   104  		ctx,
   105  		WithEtcdClient(cli),
   106  		WithStore(causetstore),
   107  		WithLease(testLease),
   108  	)
   109  	err = d1.Start(nil)
   110  	if err != nil {
   111  		t.Fatalf("DBS start failed %v", err)
   112  	}
   113  	defer d1.Stop()
   114  	if err = d1.SchemaSyncer().Init(ctx); err != nil {
   115  		t.Fatalf("schemaReplicant version syncer init failed %v", err)
   116  	}
   117  
   118  	// for watchCh
   119  	wg := sync.WaitGroup{}
   120  	wg.Add(1)
   121  	currentVer := int64(123)
   122  	var checkErr string
   123  	go func() {
   124  		defer wg.Done()
   125  		select {
   126  		case resp := <-d.SchemaSyncer().GlobalVersionCh():
   127  			if len(resp.Events) < 1 {
   128  				checkErr = "get chan events count less than 1"
   129  				return
   130  			}
   131  			checkRespKV(t, 1, DBSGlobalSchemaVersion, fmt.Sprintf("%v", currentVer), resp.Events[0].Ekv)
   132  		case <-time.After(3 * time.Second):
   133  			checkErr = "get udpate version failed"
   134  			return
   135  		}
   136  	}()
   137  
   138  	// for uFIDelate latestSchemaVersion
   139  	err = d.SchemaSyncer().TenantUFIDelateGlobalVersion(ctx, currentVer)
   140  	if err != nil {
   141  		t.Fatalf("uFIDelate latest schemaReplicant version failed %v", err)
   142  	}
   143  
   144  	wg.Wait()
   145  
   146  	if checkErr != "" {
   147  		t.Fatalf(checkErr)
   148  	}
   149  
   150  	// for CheckAllVersions
   151  	childCtx, cancel := goctx.WithTimeout(ctx, 200*time.Millisecond)
   152  	err = d.SchemaSyncer().TenantCheckAllVersions(childCtx, currentVer)
   153  	if err == nil {
   154  		t.Fatalf("check result not match")
   155  	}
   156  	cancel()
   157  
   158  	// for UFIDelateSelfVersion
   159  	err = d.SchemaSyncer().UFIDelateSelfVersion(context.Background(), currentVer)
   160  	if err != nil {
   161  		t.Fatalf("uFIDelate self version failed %v", errors.ErrorStack(err))
   162  	}
   163  	err = d1.SchemaSyncer().UFIDelateSelfVersion(context.Background(), currentVer)
   164  	if err != nil {
   165  		t.Fatalf("uFIDelate self version failed %v", errors.ErrorStack(err))
   166  	}
   167  	childCtx, _ = goctx.WithTimeout(ctx, minInterval)
   168  	err = d1.SchemaSyncer().UFIDelateSelfVersion(childCtx, currentVer)
   169  	if !isTimeoutError(err) {
   170  		t.Fatalf("uFIDelate self version result not match, err %v", err)
   171  	}
   172  
   173  	// for CheckAllVersions
   174  	err = d.SchemaSyncer().TenantCheckAllVersions(context.Background(), currentVer-1)
   175  	if err != nil {
   176  		t.Fatalf("check all versions failed %v", err)
   177  	}
   178  	err = d.SchemaSyncer().TenantCheckAllVersions(context.Background(), currentVer)
   179  	if err != nil {
   180  		t.Fatalf("check all versions failed %v", err)
   181  	}
   182  	childCtx, _ = goctx.WithTimeout(ctx, minInterval)
   183  	err = d.SchemaSyncer().TenantCheckAllVersions(childCtx, currentVer)
   184  	if !isTimeoutError(err) {
   185  		t.Fatalf("check all versions result not match, err %v", err)
   186  	}
   187  
   188  	// for StartCleanWork
   189  	ttl := 10
   190  	// Make sure NeededCleanTTL > ttl, then we definitely clean the ttl.
   191  	NeededCleanTTL = int64(11)
   192  	ttlKey := "stochastik_ttl_key"
   193  	ttlVal := "stochastik_ttl_val"
   194  	stochastik, err := tenant.NewStochastik(ctx, "", cli, tenant.NewStochastikDefaultRetryCnt, ttl)
   195  	if err != nil {
   196  		t.Fatalf("new stochastik failed %v", err)
   197  	}
   198  	err = PutKVToEtcd(context.Background(), cli, 5, ttlKey, ttlVal, clientv3.WithLease(stochastik.Lease()))
   199  	if err != nil {
   200  		t.Fatalf("put ekv to etcd failed %v", err)
   201  	}
   202  	// Make sure the ttlKey is exist in etcd.
   203  	resp, err = cli.Get(ctx, ttlKey)
   204  	if err != nil {
   205  		t.Fatalf("client get version failed %v", err)
   206  	}
   207  	checkRespKV(t, 1, ttlKey, ttlVal, resp.Ekvs...)
   208  	d.SchemaSyncer().NotifyCleanExpiredPaths()
   209  	// Make sure the clean worker is done.
   210  	notifiedCnt := 1
   211  	for i := 0; i < 100; i++ {
   212  		isNotified := d.SchemaSyncer().NotifyCleanExpiredPaths()
   213  		if isNotified {
   214  			notifiedCnt++
   215  		}
   216  		// notifyCleanExpiredPathsCh's length is 1,
   217  		// so when notifiedCnt is 3, we can make sure the clean worker is done at least once.
   218  		if notifiedCnt == 3 {
   219  			break
   220  		}
   221  		time.Sleep(20 * time.Millisecond)
   222  	}
   223  	if notifiedCnt != 3 {
   224  		t.Fatal("clean worker don't finish")
   225  	}
   226  	// Make sure the ttlKey is removed in etcd.
   227  	resp, err = cli.Get(ctx, ttlKey)
   228  	if err != nil {
   229  		t.Fatalf("client get version failed %v", err)
   230  	}
   231  	checkRespKV(t, 0, ttlKey, "", resp.Ekvs...)
   232  
   233  	// for Close
   234  	resp, err = cli.Get(goctx.Background(), key)
   235  	if err != nil {
   236  		t.Fatalf("get key %s failed %v", key, err)
   237  	}
   238  	currVer := fmt.Sprintf("%v", currentVer)
   239  	checkRespKV(t, 1, key, currVer, resp.Ekvs...)
   240  	d.SchemaSyncer().Close()
   241  	resp, err = cli.Get(goctx.Background(), key)
   242  	if err != nil {
   243  		t.Fatalf("get key %s failed %v", key, err)
   244  	}
   245  	if len(resp.Ekvs) != 0 {
   246  		t.Fatalf("remove key %s failed %v", key, err)
   247  	}
   248  }
   249  
   250  func isTimeoutError(err error) bool {
   251  	if terror.ErrorEqual(err, goctx.DeadlineExceeded) || status.Code(errors.Cause(err)) == codes.DeadlineExceeded ||
   252  		terror.ErrorEqual(err, etcdserver.ErrTimeout) {
   253  		return true
   254  	}
   255  	return false
   256  }
   257  
   258  func checkRespKV(t *testing.T, ekvCount int, key, val string,
   259  	ekvs ...*mvccpb.KeyValue) {
   260  	if len(ekvs) != ekvCount {
   261  		t.Fatalf("resp key %s ekvs %v length is != %d", key, ekvs, ekvCount)
   262  	}
   263  	if ekvCount == 0 {
   264  		return
   265  	}
   266  
   267  	ekv := ekvs[0]
   268  	if string(ekv.Key) != key {
   269  		t.Fatalf("key resp %s, exported %s", ekv.Key, key)
   270  	}
   271  	if string(ekv.Value) != val {
   272  		t.Fatalf("val resp %s, exported %s", ekv.Value, val)
   273  	}
   274  }