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  }