vitess.io/vitess@v0.16.2/go/vt/topo/test/watch.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 "vitess.io/vitess/go/vt/key" 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 // waitForInitialValue waits for the initial value of 34 // keyspaces/test_keyspace/SrvKeyspace to appear, and match the 35 // provided srvKeyspace. 36 func waitForInitialValue(t *testing.T, conn topo.Conn, srvKeyspace *topodatapb.SrvKeyspace) (changes <-chan *topo.WatchData, cancel context.CancelFunc) { 37 var current *topo.WatchData 38 ctx, cancel := context.WithCancel(context.Background()) 39 start := time.Now() 40 var err error 41 for { 42 current, changes, err = conn.Watch(ctx, "keyspaces/test_keyspace/SrvKeyspace") 43 if topo.IsErrType(err, topo.NoNode) { 44 // hasn't appeared yet 45 if time.Since(start) > 10*time.Second { 46 cancel() 47 t.Fatalf("time out waiting for file to appear") 48 } 49 time.Sleep(10 * time.Millisecond) 50 continue 51 } 52 if err != nil { 53 cancel() 54 t.Fatalf("watch failed: %v", err) 55 } 56 // we got a valid result 57 break 58 } 59 got := &topodatapb.SrvKeyspace{} 60 if err := proto.Unmarshal(current.Contents, got); err != nil { 61 cancel() 62 t.Fatalf("cannot proto-unmarshal data: %v", err) 63 } 64 if !proto.Equal(got, srvKeyspace) { 65 cancel() 66 t.Fatalf("got bad data: %v expected: %v", got, srvKeyspace) 67 } 68 69 return changes, cancel 70 } 71 72 // waitForInitialValue waits for the initial value of 73 // keyspaces/test_keyspace/SrvKeyspace to appear, and match the 74 // provided srvKeyspace. 75 func waitForInitialValueRecursive(t *testing.T, conn topo.Conn, srvKeyspace *topodatapb.SrvKeyspace) (changes <-chan *topo.WatchDataRecursive, cancel context.CancelFunc, err error) { 76 var current []*topo.WatchDataRecursive 77 ctx, cancel := context.WithCancel(context.Background()) 78 start := time.Now() 79 for { 80 current, changes, err = conn.WatchRecursive(ctx, "keyspaces/test_keyspace") 81 if topo.IsErrType(err, topo.NoNode) { 82 // hasn't appeared yet 83 if time.Since(start) > 10*time.Second { 84 cancel() 85 t.Fatalf("time out waiting for file to appear") 86 } 87 time.Sleep(10 * time.Millisecond) 88 continue 89 } 90 if topo.IsErrType(err, topo.NoImplementation) { 91 // If this is not supported, skip the test 92 cancel() 93 return nil, nil, err 94 } 95 if err != nil { 96 cancel() 97 t.Fatalf("watch failed: %v", err) 98 } 99 // we got a valid result 100 break 101 } 102 got := &topodatapb.SrvKeyspace{} 103 if err := proto.Unmarshal(current[0].Contents, got); err != nil { 104 cancel() 105 t.Fatalf("cannot proto-unmarshal data: %v", err) 106 } 107 if !proto.Equal(got, srvKeyspace) { 108 cancel() 109 t.Fatalf("got bad data: %v expected: %v", got, srvKeyspace) 110 } 111 112 return changes, cancel, nil 113 } 114 115 // checkWatch runs the tests on the Watch part of the Conn API. 116 // We use a SrvKeyspace object. 117 func checkWatch(t *testing.T, ts *topo.Server) { 118 ctx, cancel := context.WithCancel(context.Background()) 119 defer cancel() 120 conn, err := ts.ConnForCell(ctx, LocalCellName) 121 if err != nil { 122 t.Fatalf("ConnForCell(test) failed: %v", err) 123 } 124 125 // start watching something that doesn't exist -> error 126 current, changes, err := conn.Watch(ctx, "keyspaces/test_keyspace/SrvKeyspace") 127 if !topo.IsErrType(err, topo.NoNode) { 128 t.Errorf("watch on missing node didn't return ErrNoNode: %v %v", current, changes) 129 } 130 131 // create some data 132 keyRange, err := key.ParseShardingSpec("-") 133 if err != nil || len(keyRange) != 1 { 134 t.Fatalf("ParseShardingSpec failed. Expected non error and only one element. Got err: %v, len(%v)", err, len(keyRange)) 135 } 136 137 srvKeyspace := &topodatapb.SrvKeyspace{ 138 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 139 { 140 ServedType: topodatapb.TabletType_PRIMARY, 141 ShardReferences: []*topodatapb.ShardReference{ 142 { 143 Name: "name", 144 KeyRange: keyRange[0], 145 }, 146 }, 147 }, 148 }, 149 } 150 if err := ts.UpdateSrvKeyspace(ctx, LocalCellName, "test_keyspace", srvKeyspace); err != nil { 151 t.Fatalf("UpdateSrvKeyspace(1): %v", err) 152 } 153 154 // start watching again, it should work 155 changes, secondCancel := waitForInitialValue(t, conn, srvKeyspace) 156 defer secondCancel() 157 158 // change the data 159 srvKeyspace.Partitions[0].ShardReferences[0].Name = "new_name" 160 if err := ts.UpdateSrvKeyspace(ctx, LocalCellName, "test_keyspace", srvKeyspace); err != nil { 161 t.Fatalf("UpdateSrvKeyspace(2): %v", err) 162 } 163 164 // Make sure we get the watch data, maybe not as first notice, 165 // but eventually. The API specifies it is possible to get duplicate 166 // notifications. 167 for { 168 wd, ok := <-changes 169 if !ok { 170 t.Fatalf("watch channel unexpectedly closed") 171 } 172 if wd.Err != nil { 173 t.Fatalf("watch interrupted: %v", wd.Err) 174 } 175 got := &topodatapb.SrvKeyspace{} 176 if err := proto.Unmarshal(wd.Contents, got); err != nil { 177 t.Fatalf("cannot proto-unmarshal data: %v", err) 178 } 179 180 if got.Partitions[0].ShardReferences[0].Name == "name" { 181 // extra first value, still good 182 continue 183 } 184 if got.Partitions[0].ShardReferences[0].Name == "new_name" { 185 // watch worked, good 186 break 187 } 188 t.Fatalf("got unknown SrvKeyspace: %v", got) 189 } 190 191 // remove the SrvKeyspace 192 if err := ts.DeleteSrvKeyspace(ctx, LocalCellName, "test_keyspace"); err != nil { 193 t.Fatalf("DeleteSrvKeyspace: %v", err) 194 } 195 196 // Make sure we get the ErrNoNode notification eventually. 197 // The API specifies it is possible to get duplicate 198 // notifications. 199 for { 200 wd, ok := <-changes 201 if !ok { 202 t.Fatalf("watch channel unexpectedly closed") 203 } 204 if topo.IsErrType(wd.Err, topo.NoNode) { 205 // good 206 break 207 } 208 if wd.Err != nil { 209 t.Fatalf("bad error returned for deletion: %v", wd.Err) 210 } 211 // we got something, better be the right value 212 got := &topodatapb.SrvKeyspace{} 213 if err := proto.Unmarshal(wd.Contents, got); err != nil { 214 t.Fatalf("cannot proto-unmarshal data: %v", err) 215 } 216 if got.Partitions[0].ShardReferences[0].Name == "new_name" { 217 // good value 218 continue 219 } 220 t.Fatalf("got unknown SrvKeyspace waiting for deletion: %v", got) 221 } 222 223 // now the channel should be closed 224 if wd, ok := <-changes; ok { 225 t.Fatalf("got unexpected event after error: %v", wd) 226 } 227 } 228 229 // checkWatchInterrupt tests we can interrupt a watch. 230 func checkWatchInterrupt(t *testing.T, ts *topo.Server) { 231 ctx := context.Background() 232 conn, err := ts.ConnForCell(ctx, LocalCellName) 233 if err != nil { 234 t.Fatalf("ConnForCell(test) failed: %v", err) 235 } 236 237 // create some data 238 keyRange, err := key.ParseShardingSpec("-") 239 if err != nil || len(keyRange) != 1 { 240 t.Fatalf("ParseShardingSpec failed. Expected non error and only one element. Got err: %v, len(%v)", err, len(keyRange)) 241 } 242 243 srvKeyspace := &topodatapb.SrvKeyspace{ 244 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 245 { 246 ServedType: topodatapb.TabletType_PRIMARY, 247 ShardReferences: []*topodatapb.ShardReference{ 248 { 249 Name: "name", 250 KeyRange: keyRange[0], 251 }, 252 }, 253 }, 254 }, 255 } 256 if err := ts.UpdateSrvKeyspace(ctx, LocalCellName, "test_keyspace", srvKeyspace); err != nil { 257 t.Fatalf("UpdateSrvKeyspace(1): %v", err) 258 } 259 260 // Start watching, it should work. 261 changes, cancel := waitForInitialValue(t, conn, srvKeyspace) 262 263 // Now cancel the watch. 264 cancel() 265 266 // Make sure we get the topo.ErrInterrupted notification eventually. 267 for { 268 wd, ok := <-changes 269 if !ok { 270 t.Fatalf("watch channel unexpectedly closed") 271 } 272 if topo.IsErrType(wd.Err, topo.Interrupted) { 273 // good 274 break 275 } 276 if wd.Err != nil { 277 t.Fatalf("bad error returned for cancellation: %v", wd.Err) 278 } 279 // we got something, better be the right value 280 got := &topodatapb.SrvKeyspace{} 281 if err := proto.Unmarshal(wd.Contents, got); err != nil { 282 t.Fatalf("cannot proto-unmarshal data: %v", err) 283 } 284 if got.Partitions[0].ShardReferences[0].Name == "name" { 285 // good value 286 continue 287 } 288 t.Fatalf("got unknown SrvKeyspace waiting for deletion: %v", got) 289 } 290 291 // Now the channel should be closed. 292 if wd, ok := <-changes; ok { 293 t.Fatalf("got unexpected event after error: %v", wd) 294 } 295 296 // And calling cancel() again should just work. 297 cancel() 298 } 299 300 // checkWatchRecursive tests we can setup a recursive watch 301 func checkWatchRecursive(t *testing.T, ts *topo.Server) { 302 ctx, cancel := context.WithCancel(context.Background()) 303 defer cancel() 304 conn, err := ts.ConnForCell(ctx, LocalCellName) 305 if err != nil { 306 t.Fatalf("ConnForCell(test) failed: %v", err) 307 } 308 309 // create some data 310 keyRange, err := key.ParseShardingSpec("-") 311 if err != nil || len(keyRange) != 1 { 312 t.Fatalf("ParseShardingSpec failed. Expected non error and only one element. Got err: %v, len(%v)", err, len(keyRange)) 313 } 314 315 srvKeyspace := &topodatapb.SrvKeyspace{ 316 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 317 { 318 ServedType: topodatapb.TabletType_PRIMARY, 319 ShardReferences: []*topodatapb.ShardReference{ 320 { 321 Name: "name", 322 KeyRange: keyRange[0], 323 }, 324 }, 325 }, 326 }, 327 } 328 if err := ts.UpdateSrvKeyspace(ctx, LocalCellName, "test_keyspace", srvKeyspace); err != nil { 329 t.Fatalf("UpdateSrvKeyspace(1): %v", err) 330 } 331 332 // start watching again, it should work 333 changes, secondCancel, err := waitForInitialValueRecursive(t, conn, srvKeyspace) 334 if topo.IsErrType(err, topo.NoImplementation) { 335 // Skip the rest if there's no implementation 336 t.Logf("%T does not support WatchRecursive()", conn) 337 return 338 } 339 defer secondCancel() 340 341 // change the data 342 srvKeyspace.Partitions[0].ShardReferences[0].Name = "new_name" 343 if err := ts.UpdateSrvKeyspace(ctx, LocalCellName, "test_keyspace", srvKeyspace); err != nil { 344 t.Fatalf("UpdateSrvKeyspace(2): %v", err) 345 } 346 347 // Make sure we get the watch data, maybe not as first notice, 348 // but eventually. The API specifies it is possible to get duplicate 349 // notifications. 350 for { 351 wd, ok := <-changes 352 if !ok { 353 t.Fatalf("watch channel unexpectedly closed") 354 } 355 if wd.Err != nil { 356 t.Fatalf("watch interrupted: %v", wd.Err) 357 } 358 got := &topodatapb.SrvKeyspace{} 359 if err := proto.Unmarshal(wd.Contents, got); err != nil { 360 t.Fatalf("cannot proto-unmarshal data: %v", err) 361 } 362 363 if got.Partitions[0].ShardReferences[0].Name == "name" { 364 // extra first value, still good 365 continue 366 } 367 if got.Partitions[0].ShardReferences[0].Name == "new_name" { 368 // watch worked, good 369 break 370 } 371 t.Fatalf("got unknown SrvKeyspace: %v", got) 372 } 373 374 // remove the SrvKeyspace 375 if err := ts.DeleteSrvKeyspace(ctx, LocalCellName, "test_keyspace"); err != nil { 376 t.Fatalf("DeleteSrvKeyspace: %v", err) 377 } 378 379 // Make sure we get the ErrNoNode notification eventually. 380 // The API specifies it is possible to get duplicate 381 // notifications. 382 for { 383 wd, ok := <-changes 384 if !ok { 385 t.Fatalf("watch channel unexpectedly closed") 386 } 387 388 if topo.IsErrType(wd.Err, topo.NoNode) { 389 // good 390 break 391 } 392 if wd.Err != nil { 393 t.Fatalf("bad error returned for deletion: %v", wd.Err) 394 } 395 // we got something, better be the right value 396 got := &topodatapb.SrvKeyspace{} 397 if err := proto.Unmarshal(wd.Contents, got); err != nil { 398 t.Fatalf("cannot proto-unmarshal data: %v", err) 399 } 400 if got.Partitions[0].ShardReferences[0].Name == "new_name" { 401 // good value 402 continue 403 } 404 t.Fatalf("got unknown SrvKeyspace waiting for deletion: %v", got) 405 } 406 407 // We now have to stop watching. This doesn't automatically 408 // happen for recursive watches on a single file since others 409 // can still be seen. 410 secondCancel() 411 412 // Make sure we get the topo.ErrInterrupted notification eventually. 413 for { 414 wd, ok := <-changes 415 if !ok { 416 t.Fatalf("watch channel unexpectedly closed") 417 } 418 if topo.IsErrType(wd.Err, topo.Interrupted) { 419 // good 420 break 421 } 422 if wd.Err != nil { 423 t.Fatalf("bad error returned for cancellation: %v", wd.Err) 424 } 425 // we got something, better be the right value 426 got := &topodatapb.SrvKeyspace{} 427 if err := proto.Unmarshal(wd.Contents, got); err != nil { 428 t.Fatalf("cannot proto-unmarshal data: %v", err) 429 } 430 if got.Partitions[0].ShardReferences[0].Name == "name" { 431 // good value 432 continue 433 } 434 t.Fatalf("got unknown SrvKeyspace waiting for deletion: %v", got) 435 } 436 437 // Now the channel should be closed. 438 if wd, ok := <-changes; ok { 439 t.Fatalf("got unexpected event after error: %v", wd) 440 } 441 442 // And calling cancel() again should just work. 443 secondCancel() 444 }