vitess.io/vitess@v0.16.2/go/vt/topo/test/shard.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package test 18 19 import ( 20 "context" 21 "testing" 22 "time" 23 24 "github.com/stretchr/testify/require" 25 26 "google.golang.org/protobuf/proto" 27 28 "vitess.io/vitess/go/vt/topo" 29 30 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 31 ) 32 33 // checkShard verifies the Shard operations work correctly 34 func checkShard(t *testing.T, ts *topo.Server) { 35 ctx := context.Background() 36 if err := ts.CreateKeyspace(ctx, "test_keyspace", &topodatapb.Keyspace{}); err != nil { 37 t.Fatalf("CreateKeyspace: %v", err) 38 } 39 40 // Check GetShardNames returns [], nil for existing keyspace with no shards. 41 if names, err := ts.GetShardNames(ctx, "test_keyspace"); err != nil || len(names) != 0 { 42 t.Errorf("GetShardNames(keyspace with no shards) didn't return [] nil: %v %v", names, err) 43 } 44 45 if err := ts.CreateShard(ctx, "test_keyspace", "b0-c0"); err != nil { 46 t.Fatalf("CreateShard: %v", err) 47 } 48 if err := ts.CreateShard(ctx, "test_keyspace", "b0-c0"); !topo.IsErrType(err, topo.NodeExists) { 49 t.Errorf("CreateShard called second time, got: %v", err) 50 } 51 52 // Delete shard and see if we can re-create it. 53 if err := ts.DeleteShard(ctx, "test_keyspace", "b0-c0"); err != nil { 54 t.Fatalf("DeleteShard: %v", err) 55 } 56 if err := ts.DeleteShard(ctx, "test_keyspace", "b0-c0"); !topo.IsErrType(err, topo.NoNode) { 57 t.Errorf("DeleteShard(again): %v", err) 58 } 59 if err := ts.CreateShard(ctx, "test_keyspace", "b0-c0"); err != nil { 60 t.Fatalf("CreateShard: %v", err) 61 } 62 63 // Test getting an invalid shard returns ErrNoNode. 64 if _, err := ts.GetShard(ctx, "test_keyspace", "666"); !topo.IsErrType(err, topo.NoNode) { 65 t.Errorf("GetShard(666): %v", err) 66 } 67 68 // Test UpdateShardFields works. 69 other := &topodatapb.TabletAlias{Cell: "ny", Uid: 82873} 70 _, err := ts.UpdateShardFields(ctx, "test_keyspace", "b0-c0", func(si *topo.ShardInfo) error { 71 si.PrimaryAlias = other 72 return nil 73 }) 74 if err != nil { 75 t.Fatalf("UpdateShardFields error: %v", err) 76 } 77 78 si, err := ts.GetShard(ctx, "test_keyspace", "b0-c0") 79 if err != nil { 80 t.Fatalf("GetShard: %v", err) 81 } 82 if !proto.Equal(si.Shard.PrimaryAlias, other) { 83 t.Fatalf("shard.PrimaryAlias = %v, want %v", si.Shard.PrimaryAlias, other) 84 } 85 86 // test GetShardNames 87 shards, err := ts.GetShardNames(ctx, "test_keyspace") 88 if err != nil { 89 t.Errorf("GetShardNames: %v", err) 90 } 91 if len(shards) != 1 || shards[0] != "b0-c0" { 92 t.Errorf(`GetShardNames: want [ "b0-c0" ], got %v`, shards) 93 } 94 95 if _, err := ts.GetShardNames(ctx, "test_keyspace666"); !topo.IsErrType(err, topo.NoNode) { 96 t.Errorf("GetShardNames(666): %v", err) 97 } 98 } 99 100 // checkShardWithLock verifies that `TryLockShard` will keep failing with `NodeExists` error if there is 101 // a lock already taken for given shard. Once we unlock that shard, then subsequent call to `TryLockShard` 102 // should succeed. 103 func checkShardWithLock(t *testing.T, ts *topo.Server) { 104 ctx := context.Background() 105 if err := ts.CreateKeyspace(ctx, "test_keyspace", &topodatapb.Keyspace{}); err != nil { 106 t.Fatalf("CreateKeyspace: %v", err) 107 } 108 109 unblock := make(chan struct{}) 110 finished := make(chan struct{}) 111 112 // Check GetShardNames returns [], nil for existing keyspace with no shards. 113 if names, err := ts.GetShardNames(ctx, "test_keyspace"); err != nil || len(names) != 0 { 114 t.Errorf("GetShardNames(keyspace with no shards) didn't return [] nil: %v %v", names, err) 115 } 116 117 if err := ts.CreateShard(ctx, "test_keyspace", "b0-c0"); err != nil { 118 t.Fatalf("CreateShard: %v", err) 119 } 120 121 _, unlock1, err := ts.LockShard(ctx, "test_keyspace", "b0-c0", "lock") 122 if err != nil { 123 t.Errorf("CreateShard called second time, got: %v", err) 124 } 125 126 duration := 10 * time.Second 127 waitUntil := time.Now().Add(duration) 128 // As soon as we're unblocked, we try to lock the keyspace. 129 go func() { 130 <-unblock 131 var isUnLocked1 = false 132 for time.Now().Before(waitUntil) { 133 _, unlock2, err := ts.TryLockShard(ctx, "test_keyspace", "b0-c0", "lock") 134 // TryLockShard will fail since we already have acquired lock for `test-keyspace` 135 if err != nil { 136 if !topo.IsErrType(err, topo.NodeExists) { 137 require.Fail(t, "expected node exists during tryLockShard", err.Error()) 138 } 139 var finalErr error 140 // unlock `test-keyspace` shard. Now the subsequent call to `TryLockShard` will succeed. 141 unlock1(&finalErr) 142 isUnLocked1 = true 143 if finalErr != nil { 144 require.Fail(t, "Unlock(test_keyspace) failed", finalErr.Error()) 145 } 146 } else { 147 // unlock shard acquired through `TryLockShard` 148 unlock2(&err) 149 if err != nil { 150 require.Fail(t, "Unlock(test_keyspace) failed", err.Error()) 151 } 152 // true value of 'isUnLocked1' signify that we at-least hit 'NodeExits' once. 153 if isUnLocked1 { 154 close(finished) 155 } else { 156 require.Fail(t, "Test was expecting to hit `NodeExists` error at-least once") 157 } 158 break 159 } 160 } 161 }() 162 163 // unblock the go routine 164 close(unblock) 165 166 timeout := time.After(duration * 2) 167 select { 168 case <-finished: 169 case <-timeout: 170 t.Fatalf("Unlock(test_keyspace) timed out") 171 } 172 }