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 }