github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/stores_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 kvserver 12 13 import ( 14 "context" 15 "reflect" 16 "testing" 17 "time" 18 19 "github.com/cockroachdb/cockroach/pkg/clusterversion" 20 "github.com/cockroachdb/cockroach/pkg/gossip" 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/storage" 23 "github.com/cockroachdb/cockroach/pkg/testutils" 24 "github.com/cockroachdb/cockroach/pkg/util" 25 "github.com/cockroachdb/cockroach/pkg/util/hlc" 26 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 27 "github.com/cockroachdb/cockroach/pkg/util/log" 28 "github.com/cockroachdb/cockroach/pkg/util/stop" 29 "github.com/cockroachdb/cockroach/pkg/util/tracing" 30 "github.com/cockroachdb/errors" 31 ) 32 33 func newStores(ambientCtx log.AmbientContext, clock *hlc.Clock) *Stores { 34 return NewStores(ambientCtx, clock) 35 } 36 37 func TestStoresAddStore(t *testing.T) { 38 defer leaktest.AfterTest(t)() 39 ls := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, hlc.NewClock(hlc.UnixNano, time.Nanosecond)) 40 store := Store{ 41 Ident: &roachpb.StoreIdent{StoreID: 123}, 42 } 43 ls.AddStore(&store) 44 if !ls.HasStore(store.Ident.StoreID) { 45 t.Errorf("expected local sender to contain storeID=%d", store.Ident.StoreID) 46 } 47 if ls.HasStore(store.Ident.StoreID + 1) { 48 t.Errorf("expected local sender to not contain storeID=%d", store.Ident.StoreID+1) 49 } 50 } 51 52 func TestStoresRemoveStore(t *testing.T) { 53 defer leaktest.AfterTest(t)() 54 ls := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, hlc.NewClock(hlc.UnixNano, time.Nanosecond)) 55 56 storeID := roachpb.StoreID(89) 57 58 ls.AddStore(&Store{Ident: &roachpb.StoreIdent{StoreID: storeID}}) 59 60 ls.RemoveStore(&Store{Ident: &roachpb.StoreIdent{StoreID: storeID}}) 61 62 if ls.HasStore(storeID) { 63 t.Errorf("expted local sender to remove storeID=%d", storeID) 64 } 65 } 66 67 func TestStoresGetStoreCount(t *testing.T) { 68 defer leaktest.AfterTest(t)() 69 ls := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, hlc.NewClock(hlc.UnixNano, time.Nanosecond)) 70 if ls.GetStoreCount() != 0 { 71 t.Errorf("expected 0 stores in new local sender") 72 } 73 74 expectedCount := 10 75 for i := 0; i < expectedCount; i++ { 76 ls.AddStore(&Store{Ident: &roachpb.StoreIdent{StoreID: roachpb.StoreID(i)}}) 77 } 78 if count := ls.GetStoreCount(); count != expectedCount { 79 t.Errorf("expected store count to be %d but was %d", expectedCount, count) 80 } 81 } 82 83 func TestStoresVisitStores(t *testing.T) { 84 defer leaktest.AfterTest(t)() 85 ls := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, hlc.NewClock(hlc.UnixNano, time.Nanosecond)) 86 numStores := 10 87 for i := 0; i < numStores; i++ { 88 ls.AddStore(&Store{Ident: &roachpb.StoreIdent{StoreID: roachpb.StoreID(i)}}) 89 } 90 91 visit := make([]bool, numStores) 92 err := ls.VisitStores(func(s *Store) error { visit[s.Ident.StoreID] = true; return nil }) 93 if err != nil { 94 t.Errorf("unexpected error on visit: %s", err.Error()) 95 } 96 97 for i, visited := range visit { 98 if !visited { 99 t.Errorf("store %d was not visited", i) 100 } 101 } 102 103 errBoom := errors.New("boom") 104 if err := ls.VisitStores(func(s *Store) error { 105 return errBoom 106 }); !errors.Is(err, errBoom) { 107 t.Errorf("got unexpected error %v", err) 108 } 109 } 110 111 func TestStoresGetReplicaForRangeID(t *testing.T) { 112 defer leaktest.AfterTest(t)() 113 ctx := context.Background() 114 stopper := stop.NewStopper() 115 defer stopper.Stop(ctx) 116 117 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 118 119 ls := newStores(log.AmbientContext{}, clock) 120 numStores := 10 121 for i := 1; i <= numStores; i++ { 122 storeID := roachpb.StoreID(i) 123 rangeID := roachpb.RangeID(i) 124 replicaID := roachpb.ReplicaID(1) 125 126 memEngine := storage.NewDefaultInMem() 127 stopper.AddCloser(memEngine) 128 129 cfg := TestStoreConfig(clock) 130 cfg.Transport = NewDummyRaftTransport(cfg.Settings) 131 132 store := NewStore(ctx, cfg, memEngine, &roachpb.NodeDescriptor{NodeID: 1}) 133 // Fake-set an ident. This is usually read from the engine on store.Start() 134 // which we're not even going to call. 135 store.Ident = &roachpb.StoreIdent{StoreID: storeID} 136 ls.AddStore(store) 137 138 desc := &roachpb.RangeDescriptor{ 139 RangeID: rangeID, 140 StartKey: roachpb.RKey("a"), 141 EndKey: roachpb.RKey("b"), 142 InternalReplicas: []roachpb.ReplicaDescriptor{ 143 { 144 StoreID: storeID, 145 ReplicaID: replicaID, 146 NodeID: 1, 147 }, 148 }, 149 } 150 151 replica, err := newReplica(ctx, desc, store, replicaID) 152 if err != nil { 153 t.Fatalf("unexpected error when creating replica: %+v", err) 154 } 155 err2 := store.AddReplica(replica) 156 if err2 != nil { 157 t.Fatalf("unexpected error when adding replica: %v", err2) 158 } 159 } 160 161 // Test the case where the replica we're looking for exists. 162 rangeID1 := roachpb.RangeID(5) 163 replica1, _, err1 := ls.GetReplicaForRangeID(rangeID1) 164 if replica1 == nil { 165 t.Fatal("expected replica to be found; was nil") 166 } 167 if err1 != nil { 168 t.Fatalf("expected err to be nil; was %v", err1) 169 } 170 if replica1.RangeID != rangeID1 { 171 t.Fatalf("expected replica's range id to be %v; got %v", rangeID1, replica1.RangeID) 172 } 173 174 // Test the case where the replica we're looking for doesn't exist. 175 rangeID2 := roachpb.RangeID(1000) 176 replica2, _, err2 := ls.GetReplicaForRangeID(rangeID2) 177 if replica2 != nil { 178 t.Fatalf("expected replica to be nil; was %v", replica2) 179 } 180 expectedError := roachpb.NewRangeNotFoundError(rangeID2, 0) 181 if err2.Error() != expectedError.Error() { 182 t.Fatalf("expected err to be %v; was %v", expectedError, err2) 183 } 184 } 185 186 func TestStoresGetStore(t *testing.T) { 187 defer leaktest.AfterTest(t)() 188 ls := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, hlc.NewClock(hlc.UnixNano, time.Nanosecond)) 189 store := Store{Ident: &roachpb.StoreIdent{StoreID: 1}} 190 replica := roachpb.ReplicaDescriptor{StoreID: store.Ident.StoreID} 191 s, pErr := ls.GetStore(replica.StoreID) 192 if s != nil || pErr == nil { 193 t.Errorf("expected no stores in new local sender") 194 } 195 196 ls.AddStore(&store) 197 s, pErr = ls.GetStore(replica.StoreID) 198 if s == nil { 199 t.Errorf("expected store") 200 } else if s.Ident.StoreID != store.Ident.StoreID { 201 t.Errorf("expected storeID to be %d but was %d", 202 s.Ident.StoreID, store.Ident.StoreID) 203 } else if pErr != nil { 204 t.Errorf("expected no error, instead had err=%s", pErr) 205 } 206 } 207 208 var storeIDAlloc roachpb.StoreID 209 210 // createStores creates a slice of count stores. 211 func createStores(count int, t *testing.T) (*hlc.ManualClock, []*Store, *Stores, *stop.Stopper) { 212 stopper := stop.NewStopper() 213 manual := hlc.NewManualClock(123) 214 cfg := TestStoreConfig(hlc.NewClock(manual.UnixNano, time.Nanosecond)) 215 ls := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, cfg.Clock) 216 217 // Create two stores with ranges we care about. 218 stores := []*Store{} 219 for i := 0; i < count; i++ { 220 cfg.Transport = NewDummyRaftTransport(cfg.Settings) 221 eng := storage.NewDefaultInMem() 222 stopper.AddCloser(eng) 223 s := NewStore(context.Background(), cfg, eng, &roachpb.NodeDescriptor{NodeID: 1}) 224 storeIDAlloc++ 225 s.Ident = &roachpb.StoreIdent{StoreID: storeIDAlloc} 226 stores = append(stores, s) 227 } 228 229 return manual, stores, ls, stopper 230 } 231 232 // TestStoresGossipStorage verifies reading and writing of bootstrap info. 233 func TestStoresGossipStorage(t *testing.T) { 234 defer leaktest.AfterTest(t)() 235 manual, stores, ls, stopper := createStores(2, t) 236 defer stopper.Stop(context.Background()) 237 ls.AddStore(stores[0]) 238 239 // Verify initial read is empty. 240 var bi gossip.BootstrapInfo 241 if err := ls.ReadBootstrapInfo(&bi); err != nil { 242 t.Fatal(err) 243 } 244 if len(bi.Addresses) != 0 { 245 t.Errorf("expected empty bootstrap info: %+v", bi) 246 } 247 248 // Add a fake address and write. 249 manual.Increment(1) 250 bi.Addresses = append(bi.Addresses, util.MakeUnresolvedAddr("tcp", "127.0.0.1:8001")) 251 if err := ls.WriteBootstrapInfo(&bi); err != nil { 252 t.Fatal(err) 253 } 254 255 // Verify on read. 256 manual.Increment(1) 257 var newBI gossip.BootstrapInfo 258 if err := ls.ReadBootstrapInfo(&newBI); err != nil { 259 t.Fatal(err) 260 } 261 if len(newBI.Addresses) != 1 { 262 t.Errorf("expected single bootstrap info address: %+v", newBI) 263 } 264 265 // Add another store and verify it has bootstrap info written. 266 ls.AddStore(stores[1]) 267 268 // Create a new stores object to verify read. 269 ls2 := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, ls.clock) 270 ls2.AddStore(stores[1]) 271 var verifyBI gossip.BootstrapInfo 272 if err := ls2.ReadBootstrapInfo(&verifyBI); err != nil { 273 t.Fatal(err) 274 } 275 if !reflect.DeepEqual(bi, verifyBI) { 276 t.Errorf("bootstrap info %+v not equal to expected %+v", verifyBI, bi) 277 } 278 } 279 280 // TestStoresGossipStorageReadLatest verifies that the latest 281 // bootstrap info from multiple stores is returned on Read. 282 func TestStoresGossipStorageReadLatest(t *testing.T) { 283 defer leaktest.AfterTest(t)() 284 manual, stores, ls, stopper := createStores(2, t) 285 defer stopper.Stop(context.Background()) 286 ls.AddStore(stores[0]) 287 288 // Add a fake address and write. 289 var bi gossip.BootstrapInfo 290 bi.Addresses = append(bi.Addresses, util.MakeUnresolvedAddr("tcp", "127.0.0.1:8001")) 291 if err := ls.WriteBootstrapInfo(&bi); err != nil { 292 t.Fatal(err) 293 } 294 295 // Now remove store 0 and add store 1. 296 ls.RemoveStore(stores[0]) 297 ls.AddStore(stores[1]) 298 299 // Increment clock, add another address and write. 300 manual.Increment(1) 301 bi.Addresses = append(bi.Addresses, util.MakeUnresolvedAddr("tcp", "127.0.0.1:8002")) 302 if err := ls.WriteBootstrapInfo(&bi); err != nil { 303 t.Fatal(err) 304 } 305 306 // Create a new stores object to freshly read. Should get latest 307 // version from store 1. 308 manual.Increment(1) 309 ls2 := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, ls.clock) 310 ls2.AddStore(stores[0]) 311 ls2.AddStore(stores[1]) 312 var verifyBI gossip.BootstrapInfo 313 if err := ls2.ReadBootstrapInfo(&verifyBI); err != nil { 314 t.Fatal(err) 315 } 316 if !reflect.DeepEqual(bi, verifyBI) { 317 t.Errorf("bootstrap info %+v not equal to expected %+v", verifyBI, bi) 318 } 319 320 // Verify that stores[0], which had old info, was updated with 321 // latest bootstrap info during the read. 322 ls3 := newStores(log.AmbientContext{Tracer: tracing.NewTracer()}, ls.clock) 323 ls3.AddStore(stores[0]) 324 verifyBI.Reset() 325 if err := ls3.ReadBootstrapInfo(&verifyBI); err != nil { 326 t.Fatal(err) 327 } 328 if !reflect.DeepEqual(bi, verifyBI) { 329 t.Errorf("bootstrap info %+v not equal to expected %+v", verifyBI, bi) 330 } 331 } 332 333 // TestStoresClusterVersionWriteSynthesize verifies that the cluster version is 334 // written to all stores and that missing versions are filled in appropriately. 335 func TestClusterVersionWriteSynthesize(t *testing.T) { 336 defer leaktest.AfterTest(t)() 337 _, stores, _, stopper := createStores(3, t) 338 ctx := context.Background() 339 defer stopper.Stop(ctx) 340 341 v1_0 := roachpb.Version{Major: 1} 342 // Hard-code binaryVersion of 1.1 for this test. 343 // Hard-code binaryMinSupportedVersion of 1.0 for this test. 344 binV := roachpb.Version{Major: 1, Minor: 1} 345 minV := v1_0 346 347 makeStores := func() *Stores { 348 ls := NewStores(log.AmbientContext{}, stores[0].Clock()) 349 return ls 350 } 351 352 ls0 := makeStores() 353 354 // If there are no stores, default to binaryMinSupportedVersion 355 // (v1_0 in this test) 356 if initialCV, err := SynthesizeClusterVersionFromEngines(ctx, ls0.engines(), binV, minV); err != nil { 357 t.Fatal(err) 358 } else { 359 expCV := clusterversion.ClusterVersion{ 360 Version: v1_0, 361 } 362 if !reflect.DeepEqual(initialCV, expCV) { 363 t.Fatalf("expected %+v; got %+v", expCV, initialCV) 364 } 365 } 366 367 ls0.AddStore(stores[0]) 368 369 versionA := roachpb.Version{Major: 1, Minor: 0, Unstable: 1} // 1.0-1 370 versionB := roachpb.Version{Major: 1, Minor: 0, Unstable: 2} // 1.0-2 371 372 // Verify that the initial read of an empty store synthesizes v1.0-0. This 373 // is the code path that runs after starting the 1.1 binary for the first 374 // time after the rolling upgrade from 1.0. 375 if initialCV, err := SynthesizeClusterVersionFromEngines(ctx, ls0.engines(), binV, minV); err != nil { 376 t.Fatal(err) 377 } else { 378 expCV := clusterversion.ClusterVersion{ 379 Version: v1_0, 380 } 381 if !reflect.DeepEqual(initialCV, expCV) { 382 t.Fatalf("expected %+v; got %+v", expCV, initialCV) 383 } 384 } 385 386 // Bump a version to something more modern (but supported by this binary). 387 // Note that there's still only one store. 388 { 389 cv := clusterversion.ClusterVersion{ 390 Version: versionB, 391 } 392 if err := WriteClusterVersionToEngines(ctx, ls0.engines(), cv); err != nil { 393 t.Fatal(err) 394 } 395 396 // Verify the same thing comes back on read. 397 if newCV, err := SynthesizeClusterVersionFromEngines(ctx, ls0.engines(), binV, minV); err != nil { 398 t.Fatal(err) 399 } else { 400 expCV := cv 401 if !reflect.DeepEqual(newCV, cv) { 402 t.Fatalf("expected %+v; got %+v", expCV, newCV) 403 } 404 } 405 } 406 407 // Make a stores with store0 and store1. It reads as v1.0 because store1 has 408 // no entry, lowering the use version to v1.0 (but not the min version). 409 { 410 ls01 := makeStores() 411 ls01.AddStore(stores[0]) 412 ls01.AddStore(stores[1]) 413 414 expCV := clusterversion.ClusterVersion{ 415 Version: v1_0, 416 } 417 if cv, err := SynthesizeClusterVersionFromEngines(ctx, ls01.engines(), binV, minV); err != nil { 418 t.Fatal(err) 419 } else if !reflect.DeepEqual(cv, expCV) { 420 t.Fatalf("expected %+v, got %+v", expCV, cv) 421 } 422 423 // Write an updated Version to both stores. 424 cv := clusterversion.ClusterVersion{ 425 Version: versionB, 426 } 427 if err := WriteClusterVersionToEngines(ctx, ls01.engines(), cv); err != nil { 428 t.Fatal(err) 429 } 430 } 431 432 // Third node comes along, for now it's alone. It has a lower use version. 433 cv := clusterversion.ClusterVersion{ 434 Version: versionA, 435 } 436 437 { 438 ls3 := makeStores() 439 ls3.AddStore(stores[2]) 440 if err := WriteClusterVersionToEngines(ctx, ls3.engines(), cv); err != nil { 441 t.Fatal(err) 442 } 443 } 444 445 ls012 := makeStores() 446 for _, store := range stores { 447 ls012.AddStore(store) 448 } 449 450 // Reading across all stores, we expect to pick up the lowest useVersion both 451 // from the third store. 452 expCV := clusterversion.ClusterVersion{ 453 Version: versionA, 454 } 455 if cv, err := SynthesizeClusterVersionFromEngines(ctx, ls012.engines(), binV, minV); err != nil { 456 t.Fatal(err) 457 } else if !reflect.DeepEqual(cv, expCV) { 458 t.Fatalf("expected %+v, got %+v", expCV, cv) 459 } 460 } 461 462 // TestStoresClusterVersionIncompatible verifies an error occurs when 463 // setting up the cluster version from stores that are incompatible with the 464 // running binary. 465 func TestStoresClusterVersionIncompatible(t *testing.T) { 466 defer leaktest.AfterTest(t)() 467 468 ctx := context.Background() 469 470 vOneDashOne := roachpb.Version{Major: 1, Unstable: 1} 471 vOne := roachpb.Version{Major: 1} 472 473 type testCase struct { 474 binV, minV roachpb.Version // binary version and min supported version 475 engV roachpb.Version // version found on engine in test 476 expErr string 477 } 478 for name, tc := range map[string]testCase{ 479 "StoreTooNew": { 480 // This is what the node is running. 481 binV: vOneDashOne, 482 // This is what the running node requires from its stores. 483 minV: vOne, 484 // Version is way too high for this node. 485 engV: roachpb.Version{Major: 9}, 486 expErr: `cockroach version v1\.0-1 is incompatible with data in store <no-attributes>=<in-mem>; use version v9\.0 or later`, 487 }, 488 "StoreTooOldVersion": { 489 // This is what the node is running. 490 binV: roachpb.Version{Major: 9}, 491 // This is what the running node requires from its stores. 492 minV: roachpb.Version{Major: 5}, 493 // Version is way too low. 494 engV: roachpb.Version{Major: 4}, 495 expErr: `store <no-attributes>=<in-mem>, last used with cockroach version v4\.0, is too old for running version v9\.0 \(which requires data from v5\.0 or later\)`, 496 }, 497 "StoreTooOldMinVersion": { 498 // Like the previous test case, but this time cv.MinimumVersion is the culprit. 499 binV: roachpb.Version{Major: 9}, 500 minV: roachpb.Version{Major: 5}, 501 engV: roachpb.Version{Major: 4}, 502 expErr: `store <no-attributes>=<in-mem>, last used with cockroach version v4\.0, is too old for running version v9\.0 \(which requires data from v5\.0 or later\)`, 503 }, 504 } { 505 t.Run(name, func(t *testing.T) { 506 engs := []storage.Engine{storage.NewDefaultInMem()} 507 defer engs[0].Close() 508 // Configure versions and write. 509 cv := clusterversion.ClusterVersion{Version: tc.engV} 510 if err := WriteClusterVersionToEngines(ctx, engs, cv); err != nil { 511 t.Fatal(err) 512 } 513 if cv, err := SynthesizeClusterVersionFromEngines( 514 ctx, engs, tc.binV, tc.minV, 515 ); !testutils.IsError(err, tc.expErr) { 516 t.Fatalf("unexpected error: %+v, got version %v", err, cv) 517 } 518 }) 519 } 520 }