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 }