vitess.io/vitess@v0.16.2/go/vt/topo/topotests/shard_watch_test.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 agreedto 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 topotests 18 19 import ( 20 "context" 21 "strings" 22 "testing" 23 "time" 24 25 "google.golang.org/protobuf/proto" 26 27 "vitess.io/vitess/go/vt/topo" 28 "vitess.io/vitess/go/vt/topo/memorytopo" 29 30 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 31 ) 32 33 // waitForInitialShard waits for the initial Shard to appear. 34 func waitForInitialShard(t *testing.T, ts *topo.Server, keyspace, shard string) (current *topo.WatchShardData, changes <-chan *topo.WatchShardData, cancel context.CancelFunc) { 35 ctx, cancel := context.WithCancel(context.Background()) 36 start := time.Now() 37 var err error 38 for { 39 current, changes, err = ts.WatchShard(ctx, keyspace, shard) 40 switch { 41 case topo.IsErrType(err, topo.NoNode): 42 // hasn't appeared yet 43 if time.Since(start) > 10*time.Second { 44 t.Fatalf("time out waiting for file to appear") 45 } 46 time.Sleep(10 * time.Millisecond) 47 continue 48 case err == nil: 49 return 50 default: 51 t.Fatalf("watch failed: %v", err) 52 } 53 } 54 } 55 56 func TestWatchShardNoNode(t *testing.T) { 57 keyspace := "ks1" 58 shard := "0" 59 ctx := context.Background() 60 ts := memorytopo.NewServer("cell1") 61 62 // No Shard -> ErrNoNode 63 _, _, err := ts.WatchShard(ctx, keyspace, shard) 64 if !topo.IsErrType(err, topo.NoNode) { 65 t.Errorf("Got invalid result from WatchShard(not there): %v", err) 66 } 67 } 68 69 func TestWatchShard(t *testing.T) { 70 cell := "cell1" 71 keyspace := "ks1" 72 shard := "0" 73 ctx := context.Background() 74 ts := memorytopo.NewServer(cell) 75 76 // Create keyspace 77 if err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{}); err != nil { 78 t.Fatalf("CreateKeyspace %v failed: %v", keyspace, err) 79 } 80 81 // Create initial value 82 if err := ts.CreateShard(ctx, keyspace, shard); err != nil { 83 t.Fatalf("Create(/keyspaces/ks1/shards/0/Shard) failed: %v", err) 84 } 85 86 // Starting the watch should now work, and return an empty 87 // Shard. 88 // Shards are always created with IsPrimaryServing true 89 wanted := &topodatapb.Shard{IsPrimaryServing: true} 90 current, changes, cancel := waitForInitialShard(t, ts, keyspace, shard) 91 if !proto.Equal(current.Value, wanted) { 92 t.Fatalf("got bad data: %v expected: %v", current.Value, wanted) 93 } 94 95 // Update the value with good data, wait until we see it 96 wanted.IsPrimaryServing = false 97 if _, err := ts.UpdateShardFields(ctx, keyspace, shard, func(si *topo.ShardInfo) error { 98 si.IsPrimaryServing = false 99 return nil 100 }); err != nil { 101 t.Fatalf("Update(/keyspaces/ks1/shards/0/Shard) failed: %v", err) 102 } 103 for { 104 wd, ok := <-changes 105 if !ok { 106 t.Fatalf("watch channel unexpectedly closed") 107 } 108 if wd.Err != nil { 109 t.Fatalf("watch channel unexpectedly got error: %v", wd.Err) 110 } 111 if proto.Equal(wd.Value, wanted) { 112 break 113 } 114 if proto.Equal(wd.Value, &topodatapb.Shard{}) { 115 t.Log("got duplicate empty value, skipping.") 116 } 117 t.Fatalf("got bad data: %v expected: %v", wd.Value, wanted) 118 } 119 120 conn, err := ts.ConnForCell(ctx, "global") 121 if err != nil { 122 t.Fatalf("ConnForCell failed: %v", err) 123 } 124 // Update the value with bad data, wait until error. 125 if _, err := conn.Update(ctx, "/keyspaces/"+keyspace+"/shards/"+shard+"/Shard", []byte("BAD PROTO DATA"), nil); err != nil { 126 t.Fatalf("Update(/keyspaces/ks1/shards/0/Shard) failed: %v", err) 127 } 128 for { 129 wd, ok := <-changes 130 if !ok { 131 t.Fatalf("watch channel unexpectedly closed") 132 } 133 if wd.Err != nil { 134 if strings.Contains(wd.Err.Error(), "error unpacking Shard object") { 135 break 136 } 137 t.Fatalf("watch channel unexpectedly got unknown error: %v", wd.Err) 138 } 139 if !proto.Equal(wd.Value, wanted) { 140 t.Fatalf("got bad data: %v expected: %v", wd.Value, wanted) 141 } 142 t.Log("got duplicate right value, skipping.") 143 } 144 145 // Cancel should still work here, although it does nothing. 146 cancel() 147 148 // Bad data in topo, setting the watch should now fail. 149 _, _, err = ts.WatchShard(ctx, keyspace, shard) 150 if err == nil || !strings.Contains(err.Error(), "error unpacking initial Shard object") { 151 t.Fatalf("expected an initial error setting watch on bad content, but got: %v", err) 152 } 153 154 data, err := proto.Marshal(wanted) 155 if err != nil { 156 t.Fatalf("error marshalling proto data: %v", err) 157 } 158 // Update content, wait until Watch works again 159 if _, err := conn.Update(ctx, "/keyspaces/"+keyspace+"/shards/"+shard+"/Shard", data, nil); err != nil { 160 t.Fatalf("Update(/keyspaces/ks1/shards/0/Shard) failed: %v", err) 161 } 162 start := time.Now() 163 for { 164 current, changes, err = ts.WatchShard(ctx, keyspace, shard) 165 if err != nil { 166 if strings.Contains(err.Error(), "error unpacking initial Shard object") { 167 // hasn't changed yet 168 if time.Since(start) > 10*time.Second { 169 t.Fatalf("time out waiting for file to appear") 170 } 171 time.Sleep(10 * time.Millisecond) 172 continue 173 } 174 t.Fatalf("got unexpected error while setting watch: %v", err) 175 } 176 if !proto.Equal(current.Value, wanted) { 177 t.Fatalf("got bad data: %v expected: %v", current.Value, wanted) 178 } 179 break 180 } 181 182 // Delete node, wait for error (skip any duplicate). 183 if err := ts.DeleteShard(ctx, keyspace, shard); err != nil { 184 t.Fatalf("DeleteShard() failed: %v", err) 185 } 186 for { 187 wd, ok := <-changes 188 if !ok { 189 t.Fatalf("watch channel unexpectedly closed") 190 } 191 if topo.IsErrType(wd.Err, topo.NoNode) { 192 break 193 } 194 if wd.Err != nil { 195 t.Fatalf("watch channel unexpectedly got unknown error: %v", wd.Err) 196 } 197 if !proto.Equal(wd.Value, wanted) { 198 t.Fatalf("got bad data: %v expected: %v", wd.Value, wanted) 199 } 200 t.Log("got duplicate right value, skipping.") 201 } 202 } 203 204 func TestWatchShardCancel(t *testing.T) { 205 cell := "cell1" 206 keyspace := "ks1" 207 shard := "0" 208 ctx := context.Background() 209 ts := memorytopo.NewServer(cell) 210 211 // No Shard -> ErrNoNode 212 _, _, err := ts.WatchShard(ctx, keyspace, shard) 213 if !topo.IsErrType(err, topo.NoNode) { 214 t.Errorf("Got invalid result from WatchShard(not there): %v", err) 215 } 216 217 // Create keyspace 218 if err := ts.CreateKeyspace(ctx, keyspace, &topodatapb.Keyspace{}); err != nil { 219 t.Fatalf("CreateKeyspace %v failed: %v", keyspace, err) 220 } 221 222 // Create initial value 223 if err := ts.CreateShard(ctx, keyspace, shard); err != nil { 224 t.Fatalf("Create(/keyspaces/ks1/shards/0/Shard) failed: %v", err) 225 } 226 wanted := &topodatapb.Shard{ 227 IsPrimaryServing: false, 228 } 229 if _, err := ts.UpdateShardFields(ctx, keyspace, shard, func(si *topo.ShardInfo) error { 230 si.IsPrimaryServing = false 231 return nil 232 }); err != nil { 233 t.Fatalf("UpdateShardFields() failed: %v", err) 234 } 235 236 // Starting the watch should now work. 237 current, changes, cancel := waitForInitialShard(t, ts, keyspace, shard) 238 if !proto.Equal(current.Value, wanted) { 239 t.Fatalf("got bad data: %v expected: %v", current.Value, wanted) 240 } 241 242 // Cancel watch, wait for error. 243 cancel() 244 for { 245 wd, ok := <-changes 246 if !ok { 247 t.Fatalf("watch channel unexpectedly closed") 248 } 249 if topo.IsErrType(wd.Err, topo.Interrupted) { 250 break 251 } 252 if wd.Err != nil { 253 t.Fatalf("watch channel unexpectedly got unknown error: %v", wd.Err) 254 } 255 if !proto.Equal(wd.Value, wanted) { 256 t.Fatalf("got bad data: %v expected: %v", wd.Value, wanted) 257 } 258 t.Log("got duplicate right value, skipping.") 259 } 260 261 // Cancel should still work here, although it does nothing. 262 cancel() 263 }