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  }