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  }