github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/gossip/infostore_test.go (about)

     1  // Copyright 2014 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package gossip
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"math"
    17  	"reflect"
    18  	"sort"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/cockroachdb/cockroach/pkg/base"
    24  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    25  	"github.com/cockroachdb/cockroach/pkg/util"
    26  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    27  	"github.com/cockroachdb/cockroach/pkg/util/log"
    28  	"github.com/cockroachdb/cockroach/pkg/util/metric"
    29  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    30  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    31  	"github.com/cockroachdb/cockroach/pkg/util/tracing"
    32  	"github.com/gogo/protobuf/proto"
    33  )
    34  
    35  var emptyAddr = util.MakeUnresolvedAddr("test", "<test-addr>")
    36  
    37  func newTestInfoStore() (*infoStore, *stop.Stopper) {
    38  	stopper := stop.NewStopper()
    39  	nc := &base.NodeIDContainer{}
    40  	nc.Set(context.Background(), 1)
    41  	is := newInfoStore(log.AmbientContext{Tracer: tracing.NewTracer()}, nc, emptyAddr, stopper)
    42  	return is, stopper
    43  }
    44  
    45  // TestZeroDuration verifies that specifying a zero duration sets
    46  // TTLStamp to max int64.
    47  func TestZeroDuration(t *testing.T) {
    48  	defer leaktest.AfterTest(t)()
    49  	is, stopper := newTestInfoStore()
    50  	defer stopper.Stop(context.Background())
    51  	info := is.newInfo(nil, 0)
    52  	if info.TTLStamp != math.MaxInt64 {
    53  		t.Errorf("expected zero duration to get max TTLStamp: %d", info.TTLStamp)
    54  	}
    55  }
    56  
    57  // TestNewInfo creates new info objects. Verify sequence increments.
    58  func TestNewInfo(t *testing.T) {
    59  	defer leaktest.AfterTest(t)()
    60  	is, stopper := newTestInfoStore()
    61  	defer stopper.Stop(context.Background())
    62  	info1 := is.newInfo(nil, time.Second)
    63  	info2 := is.newInfo(nil, time.Second)
    64  	if err := is.addInfo("a", info1); err != nil {
    65  		t.Error(err)
    66  	}
    67  	if err := is.addInfo("b", info2); err != nil {
    68  		t.Error(err)
    69  	}
    70  	if info1.OrigStamp >= info2.OrigStamp {
    71  		t.Errorf("timestamps should increment %d, %d", info1.OrigStamp, info2.OrigStamp)
    72  	}
    73  }
    74  
    75  // TestInfoStoreGetInfo adds an info, and makes sure it can be fetched
    76  // via getInfo. Also, verifies a non-existent info can't be fetched.
    77  func TestInfoStoreGetInfo(t *testing.T) {
    78  	defer leaktest.AfterTest(t)()
    79  	is, stopper := newTestInfoStore()
    80  	defer stopper.Stop(context.Background())
    81  	i := is.newInfo(nil, time.Second)
    82  	i.NodeID = 1
    83  	if err := is.addInfo("a", i); err != nil {
    84  		t.Error(err)
    85  	}
    86  	if infoCount := len(is.Infos); infoCount != 1 {
    87  		t.Errorf("infostore count incorrect %d != 1", infoCount)
    88  	}
    89  	if is.highWaterStamps[1] != i.OrigStamp {
    90  		t.Error("high water timestamps map wasn't updated")
    91  	}
    92  	if is.getInfo("a") != i {
    93  		t.Error("unable to get info")
    94  	}
    95  	if is.getInfo("b") != nil {
    96  		t.Error("erroneously produced non-existent info for key b")
    97  	}
    98  }
    99  
   100  // Verify TTL is respected on info fetched by key.
   101  func TestInfoStoreGetInfoTTL(t *testing.T) {
   102  	defer leaktest.AfterTest(t)()
   103  	is, stopper := newTestInfoStore()
   104  	defer stopper.Stop(context.Background())
   105  	i := is.newInfo(nil, time.Nanosecond)
   106  	if err := is.addInfo("a", i); err != nil {
   107  		t.Error(err)
   108  	}
   109  	time.Sleep(time.Nanosecond)
   110  	if info := is.getInfo("a"); info != nil {
   111  		t.Errorf("shouldn't be able to get info with short TTL, got %+v", info)
   112  	}
   113  }
   114  
   115  // Add infos using same key, same and lesser timestamp; verify no
   116  // replacement.
   117  func TestAddInfoSameKeyLessThanEqualTimestamp(t *testing.T) {
   118  	defer leaktest.AfterTest(t)()
   119  	is, stopper := newTestInfoStore()
   120  	defer stopper.Stop(context.Background())
   121  	info1 := is.newInfo(nil, time.Second)
   122  	if err := is.addInfo("a", info1); err != nil {
   123  		t.Error(err)
   124  	}
   125  	info2 := is.newInfo(nil, time.Second)
   126  	info2.Value.Timestamp.WallTime = info1.Value.Timestamp.WallTime
   127  	if err := is.addInfo("a", info2); err == nil {
   128  		t.Error("able to add info2 with same timestamp")
   129  	}
   130  	info2.Value.Timestamp.WallTime--
   131  	if err := is.addInfo("a", info2); err == nil {
   132  		t.Error("able to add info2 with lesser timestamp")
   133  	}
   134  	// Verify info2 did not replace info1.
   135  	if is.getInfo("a") != info1 {
   136  		t.Error("info1 was replaced, despite same timestamp")
   137  	}
   138  }
   139  
   140  // Add infos using same key, same timestamp; verify no replacement.
   141  func TestAddInfoSameKeyGreaterTimestamp(t *testing.T) {
   142  	defer leaktest.AfterTest(t)()
   143  	is, stopper := newTestInfoStore()
   144  	defer stopper.Stop(context.Background())
   145  	info1 := is.newInfo(nil, time.Second)
   146  	info2 := is.newInfo(nil, time.Second)
   147  	if err1, err2 := is.addInfo("a", info1), is.addInfo("a", info2); err1 != nil || err2 != nil {
   148  		t.Error(err1, err2)
   149  	}
   150  }
   151  
   152  // Verify that adding two infos with different hops but same keys
   153  // always chooses the minimum hops.
   154  func TestAddInfoSameKeyDifferentHops(t *testing.T) {
   155  	defer leaktest.AfterTest(t)()
   156  	is, stopper := newTestInfoStore()
   157  	defer stopper.Stop(context.Background())
   158  	info1 := is.newInfo(nil, time.Second)
   159  	info1.Hops = 1
   160  	info2 := is.newInfo(nil, time.Second)
   161  	info2.Value.Timestamp.WallTime = info1.Value.Timestamp.WallTime
   162  	info2.Hops = 2
   163  	if err := is.addInfo("a", info1); err != nil {
   164  		t.Errorf("failed insert: %s", err)
   165  	}
   166  	if err := is.addInfo("a", info2); err == nil {
   167  		t.Errorf("shouldn't have inserted info 2: %s", err)
   168  	}
   169  
   170  	i := is.getInfo("a")
   171  	if i.Hops != info1.Hops || !proto.Equal(i, info1) {
   172  		t.Error("failed to properly combine hops and value", i)
   173  	}
   174  
   175  	// Try yet another info, with lower hops yet (0).
   176  	info3 := is.newInfo(nil, time.Second)
   177  	if err := is.addInfo("a", info3); err != nil {
   178  		t.Error(err)
   179  	}
   180  	i = is.getInfo("a")
   181  	if i.Hops != info3.Hops || !proto.Equal(i, info3) {
   182  		t.Error("failed to properly combine hops and value", i)
   183  	}
   184  }
   185  
   186  func TestCombineInfosRatchetMonotonic(t *testing.T) {
   187  	defer leaktest.AfterTest(t)()
   188  
   189  	for _, local := range []bool{true, false} {
   190  		t.Run(fmt.Sprintf("local=%t", local), func(t *testing.T) {
   191  			is, stopper := newTestInfoStore()
   192  			defer stopper.Stop(context.Background())
   193  
   194  			// Generate an info with a timestamp in the future.
   195  			info := &Info{
   196  				NodeID:    is.nodeID.Get(),
   197  				TTLStamp:  math.MaxInt64,
   198  				OrigStamp: monotonicUnixNano() + int64(time.Hour),
   199  			}
   200  			if !local {
   201  				info.NodeID++
   202  			}
   203  
   204  			// Reset the monotonic clock.
   205  			monoTime.Lock()
   206  			monoTime.last = 0
   207  			monoTime.Unlock()
   208  
   209  			fresh, err := is.combine(map[string]*Info{"hello": info}, 2)
   210  			if err != nil {
   211  				t.Fatal(err)
   212  			}
   213  			if fresh != 1 {
   214  				t.Fatalf("expected no infos to be added, but found %d", fresh)
   215  			}
   216  
   217  			// Verify the monotonic clock was ratcheted if the info was generated
   218  			// locally.
   219  			monoTime.Lock()
   220  			last := monoTime.last
   221  			monoTime.Unlock()
   222  			var expectedLast int64
   223  			if local {
   224  				expectedLast = info.OrigStamp
   225  				if now := monotonicUnixNano(); now <= last {
   226  					t.Fatalf("expected mono-time to increase: %d <= %d", now, last)
   227  				}
   228  			}
   229  			if expectedLast != last {
   230  				t.Fatalf("expected mono-time %d, but found %d", expectedLast, last)
   231  			}
   232  
   233  			if i := is.getInfo("hello"); i == nil {
   234  				t.Fatalf("expected to find info\n%v", is.Infos)
   235  			}
   236  		})
   237  	}
   238  }
   239  
   240  // Helper method creates an infostore with 10 infos.
   241  func createTestInfoStore(t *testing.T) *infoStore {
   242  	is, stopper := newTestInfoStore()
   243  	defer stopper.Stop(context.Background())
   244  
   245  	for i := 0; i < 10; i++ {
   246  		infoA := is.newInfo(nil, time.Second)
   247  		infoA.NodeID = 1
   248  		infoA.Hops = 1
   249  		if err := is.addInfo(fmt.Sprintf("a.%d", i), infoA); err != nil {
   250  			t.Fatal(err)
   251  		}
   252  
   253  		infoB := is.newInfo(nil, time.Second)
   254  		infoB.NodeID = 2
   255  		infoB.Hops = 2
   256  		if err := is.addInfo(fmt.Sprintf("b.%d", i), infoB); err != nil {
   257  			t.Fatal(err)
   258  		}
   259  
   260  		infoC := is.newInfo(nil, time.Second)
   261  		infoC.NodeID = 3
   262  		infoC.Hops = 3
   263  		if err := is.addInfo(fmt.Sprintf("c.%d", i), infoC); err != nil {
   264  			t.Fatal(err)
   265  		}
   266  	}
   267  
   268  	return is
   269  }
   270  
   271  // Check infostore delta based on info high water timestamps.
   272  func TestInfoStoreDelta(t *testing.T) {
   273  	defer leaktest.AfterTest(t)()
   274  	is := createTestInfoStore(t)
   275  
   276  	// Verify deltas with successive high water timestamps & min hops.
   277  	infos := is.delta(map[roachpb.NodeID]int64{})
   278  	for i := 0; i < 10; i++ {
   279  		if i > 0 {
   280  			infoA := is.getInfo(fmt.Sprintf("a.%d", i-1))
   281  			infoB := is.getInfo(fmt.Sprintf("b.%d", i-1))
   282  			infoC := is.getInfo(fmt.Sprintf("c.%d", i-1))
   283  			infos = is.delta(map[roachpb.NodeID]int64{
   284  				1: infoA.OrigStamp,
   285  				2: infoB.OrigStamp,
   286  				3: infoC.OrigStamp,
   287  			})
   288  		}
   289  
   290  		for _, node := range []string{"a", "b", "c"} {
   291  			for j := 0; j < 10; j++ {
   292  				expected := i <= j
   293  				if _, ok := infos[fmt.Sprintf("%s.%d", node, j)]; ok != expected {
   294  					t.Errorf("i,j=%d,%d: expected to fetch info %s.%d? %t; got %t", i, j, node, j, expected, ok)
   295  				}
   296  			}
   297  		}
   298  	}
   299  
   300  	if infos := is.delta(map[roachpb.NodeID]int64{
   301  		1: math.MaxInt64,
   302  		2: math.MaxInt64,
   303  		3: math.MaxInt64,
   304  	}); len(infos) != 0 {
   305  		t.Errorf("fetching delta of infostore at maximum timestamp should return empty, got %v", infos)
   306  	}
   307  }
   308  
   309  // TestInfoStoreMostDistant verifies selection of most distant node &
   310  // associated hops.
   311  func TestInfoStoreMostDistant(t *testing.T) {
   312  	defer leaktest.AfterTest(t)()
   313  	nodes := []roachpb.NodeID{
   314  		roachpb.NodeID(1),
   315  		roachpb.NodeID(2),
   316  		roachpb.NodeID(3),
   317  	}
   318  	is, stopper := newTestInfoStore()
   319  	defer stopper.Stop(context.Background())
   320  
   321  	// Start with one very distant info that shouldn't affect mostDistant
   322  	// calculations because it isn't a node ID key.
   323  	scInfo := is.newInfo(nil, time.Second)
   324  	scInfo.Hops = 100
   325  	scInfo.NodeID = nodes[0]
   326  	if err := is.addInfo(KeySystemConfig, scInfo); err != nil {
   327  		t.Fatal(err)
   328  	}
   329  
   330  	// Add info from each address, with hop count equal to index+1.
   331  	var expectedNodeID roachpb.NodeID
   332  	var expectedHops uint32
   333  	for i := 0; i < len(nodes); i++ {
   334  		inf := is.newInfo(nil, time.Second)
   335  		inf.Hops = uint32(i + 1)
   336  		inf.NodeID = nodes[i]
   337  		if err := is.addInfo(MakeNodeIDKey(inf.NodeID), inf); err != nil {
   338  			t.Fatal(err)
   339  		}
   340  		if inf.NodeID != 1 {
   341  			expectedNodeID = inf.NodeID
   342  			expectedHops = inf.Hops
   343  		}
   344  		nodeID, hops := is.mostDistant(func(roachpb.NodeID) bool { return false })
   345  		if expectedNodeID != nodeID {
   346  			t.Errorf("%d: expected n%d; got %d", i, expectedNodeID, nodeID)
   347  		}
   348  		if expectedHops != hops {
   349  			t.Errorf("%d: expected hops %d; got %d", i, expectedHops, hops)
   350  		}
   351  	}
   352  
   353  	// Finally, simulate a Gossip instance that has an outgoing connection
   354  	// and expect the outgoing connection to not be recommended even though
   355  	// it's the furthest node away.
   356  	filteredNode := nodes[len(nodes)-1]
   357  	expectedNode := nodes[len(nodes)-2]
   358  	expectedHops = uint32(expectedNode)
   359  	nodeID, hops := is.mostDistant(func(nodeID roachpb.NodeID) bool {
   360  		return nodeID == filteredNode
   361  	})
   362  	if nodeID != expectedNode {
   363  		t.Errorf("expected n%d; got %d", expectedNode, nodeID)
   364  	}
   365  	if hops != expectedHops {
   366  		t.Errorf("expected hops %d; got %d", expectedHops, hops)
   367  	}
   368  }
   369  
   370  // TestLeastUseful verifies that the least-contributing peer node
   371  // can be determined.
   372  func TestLeastUseful(t *testing.T) {
   373  	defer leaktest.AfterTest(t)()
   374  	nodes := []roachpb.NodeID{
   375  		roachpb.NodeID(1),
   376  		roachpb.NodeID(2),
   377  	}
   378  	is, stopper := newTestInfoStore()
   379  	defer stopper.Stop(context.Background())
   380  
   381  	set := makeNodeSet(3, metric.NewGauge(metric.Metadata{Name: ""}))
   382  	if is.leastUseful(set) != 0 {
   383  		t.Error("not expecting a node from an empty set")
   384  	}
   385  
   386  	inf1 := is.newInfo(nil, time.Second)
   387  	inf1.NodeID = 1
   388  	inf1.PeerID = 1
   389  	if err := is.addInfo("a1", inf1); err != nil {
   390  		t.Fatal(err)
   391  	}
   392  	if is.leastUseful(set) != 0 {
   393  		t.Error("not expecting a node from an empty set")
   394  	}
   395  
   396  	set.addNode(nodes[0])
   397  	if is.leastUseful(set) != nodes[0] {
   398  		t.Error("expecting nodes[0] as least useful")
   399  	}
   400  
   401  	inf2 := is.newInfo(nil, time.Second)
   402  	inf2.NodeID = 2
   403  	inf2.PeerID = 1
   404  	if err := is.addInfo("a2", inf2); err != nil {
   405  		t.Fatal(err)
   406  	}
   407  	if is.leastUseful(set) != nodes[0] {
   408  		t.Error("expecting nodes[0] as least useful")
   409  	}
   410  
   411  	set.addNode(nodes[1])
   412  	if is.leastUseful(set) != nodes[1] {
   413  		t.Error("expecting nodes[1] as least useful")
   414  	}
   415  
   416  	inf3 := is.newInfo(nil, time.Second)
   417  	inf3.NodeID = 2
   418  	inf3.PeerID = 2
   419  	if err := is.addInfo("a3", inf3); err != nil {
   420  		t.Fatal(err)
   421  	}
   422  	if is.leastUseful(set) != nodes[1] {
   423  		t.Error("expecting nodes[1] as least useful")
   424  	}
   425  }
   426  
   427  type callbackRecord struct {
   428  	keys []string
   429  	wg   *sync.WaitGroup
   430  	syncutil.Mutex
   431  }
   432  
   433  func (cr *callbackRecord) Add(key string, _ roachpb.Value) {
   434  	cr.Lock()
   435  	defer cr.Unlock()
   436  	cr.keys = append(cr.keys, key)
   437  	cr.wg.Done()
   438  }
   439  
   440  func (cr *callbackRecord) Keys() []string {
   441  	cr.Lock()
   442  	defer cr.Unlock()
   443  	return append([]string(nil), cr.keys...)
   444  }
   445  
   446  func TestCallbacks(t *testing.T) {
   447  	defer leaktest.AfterTest(t)()
   448  	is, stopper := newTestInfoStore()
   449  	defer stopper.Stop(context.Background())
   450  	wg := &sync.WaitGroup{}
   451  	cb1 := callbackRecord{wg: wg}
   452  	cb2 := callbackRecord{wg: wg}
   453  	cbAll := callbackRecord{wg: wg}
   454  
   455  	unregisterCB1 := is.registerCallback("key1", cb1.Add)
   456  	is.registerCallback("key2", cb2.Add)
   457  	is.registerCallback("key.*", cbAll.Add, Redundant)
   458  
   459  	i1 := is.newInfo(nil, time.Second)
   460  	i2 := is.newInfo(nil, time.Second)
   461  	i3 := is.newInfo(nil, time.Second)
   462  
   463  	// Add infos twice and verify callbacks aren't called for same timestamps.
   464  	for i := 0; i < 2; i++ {
   465  		for _, test := range []struct {
   466  			key   string
   467  			info  *Info
   468  			count int
   469  		}{
   470  			{"key1", i1, 2},
   471  			{"key2", i2, 2},
   472  			{"key3", i3, 1},
   473  		} {
   474  			if i == 0 {
   475  				wg.Add(test.count)
   476  			}
   477  			if err := is.addInfo(test.key, test.info); err != nil {
   478  				if i == 0 {
   479  					t.Error(err)
   480  				}
   481  			} else if i != 0 {
   482  				t.Errorf("expected error on run #%d, but didn't get one", i)
   483  			}
   484  			wg.Wait()
   485  		}
   486  
   487  		if expKeys := []string{"key1"}; !reflect.DeepEqual(cb1.Keys(), expKeys) {
   488  			t.Errorf("expected %v, got %v", expKeys, cb1.Keys())
   489  		}
   490  		if expKeys := []string{"key2"}; !reflect.DeepEqual(cb2.Keys(), expKeys) {
   491  			t.Errorf("expected %v, got %v", expKeys, cb2.Keys())
   492  		}
   493  		keys := cbAll.Keys()
   494  		if expKeys := []string{"key1", "key2", "key3"}; !reflect.DeepEqual(keys, expKeys) {
   495  			t.Errorf("expected %v, got %v", expKeys, keys)
   496  		}
   497  	}
   498  
   499  	// Update an info twice.
   500  	for i := 0; i < 2; i++ {
   501  		i1 := is.newInfo([]byte("a"), time.Second)
   502  		// The first time both callbacks will fire because the value has
   503  		// changed. The second time cbAll (created with the Redundant option) will
   504  		// fire.
   505  		wg.Add(2 - i)
   506  		if err := is.addInfo("key1", i1); err != nil {
   507  			t.Error(err)
   508  		}
   509  		wg.Wait()
   510  
   511  		if expKeys := []string{"key1", "key1"}; !reflect.DeepEqual(cb1.Keys(), expKeys) {
   512  			t.Errorf("expected %v, got %v", expKeys, cb1.Keys())
   513  		}
   514  		if expKeys := []string{"key2"}; !reflect.DeepEqual(cb2.Keys(), expKeys) {
   515  			t.Errorf("expected %v, got %v", expKeys, cb2.Keys())
   516  		}
   517  	}
   518  
   519  	if expKeys := []string{"key1", "key2", "key3", "key1", "key1"}; !reflect.DeepEqual(cbAll.Keys(), expKeys) {
   520  		t.Errorf("expected %v, got %v", expKeys, cbAll.Keys())
   521  	}
   522  
   523  	const numInfos = 3
   524  
   525  	// Register another callback with same pattern and verify it is
   526  	// invoked for all three keys.
   527  	wg.Add(numInfos)
   528  	is.registerCallback("key.*", cbAll.Add)
   529  	wg.Wait()
   530  
   531  	expKeys := []string{"key1", "key2", "key3"}
   532  	keys := cbAll.Keys()
   533  	keys = keys[len(keys)-numInfos:]
   534  	sort.Strings(keys)
   535  	if !reflect.DeepEqual(keys, expKeys) {
   536  		t.Errorf("expected %v, got %v", expKeys, keys)
   537  	}
   538  
   539  	// Unregister a callback and verify nothing is invoked on it.
   540  	unregisterCB1()
   541  	iNew := is.newInfo([]byte("b"), time.Second)
   542  	wg.Add(2) // for the two cbAll callbacks
   543  	if err := is.addInfo("key1", iNew); err != nil {
   544  		t.Error(err)
   545  	}
   546  	wg.Wait()
   547  	if len(cb1.Keys()) != 2 {
   548  		t.Errorf("expected no new cb1 keys, got %v", cb1.Keys())
   549  	}
   550  }
   551  
   552  // TestRegisterCallback verifies that a callback is invoked when
   553  // registered if there are items which match its regexp in the
   554  // infostore.
   555  func TestRegisterCallback(t *testing.T) {
   556  	defer leaktest.AfterTest(t)()
   557  	is, stopper := newTestInfoStore()
   558  	defer stopper.Stop(context.Background())
   559  	wg := &sync.WaitGroup{}
   560  	cb := callbackRecord{wg: wg}
   561  
   562  	i1 := is.newInfo(nil, time.Second)
   563  	i2 := is.newInfo(nil, time.Second)
   564  	if err := is.addInfo("key1", i1); err != nil {
   565  		t.Fatal(err)
   566  	}
   567  	if err := is.addInfo("key2", i2); err != nil {
   568  		t.Fatal(err)
   569  	}
   570  
   571  	wg.Add(2)
   572  	is.registerCallback("key.*", cb.Add)
   573  	wg.Wait()
   574  	actKeys := cb.Keys()
   575  	sort.Strings(actKeys)
   576  	if expKeys := []string{"key1", "key2"}; !reflect.DeepEqual(actKeys, expKeys) {
   577  		t.Errorf("expected %v, got %v", expKeys, cb.Keys())
   578  	}
   579  }