func testGossipPeeringsInner( ctx context.Context, t *testing.T, c cluster.Cluster, cfg cluster.TestConfig, ) { num := c.NumNodes() deadline := timeutil.Now().Add(cfg.Duration) waitTime := longWaitTime if cfg.Duration < waitTime { waitTime = shortWaitTime } for timeutil.Now().Before(deadline) { CheckGossip(ctx, t, c, waitTime, HasPeers(num)) // Restart the first node. log.Infof(ctx, "restarting node 0") if err := c.Restart(ctx, 0); err != nil { t.Fatal(err) } CheckGossip(ctx, t, c, waitTime, HasPeers(num)) // Restart another node (if there is one). var pickedNode int if num > 1 { pickedNode = rand.Intn(num-1) + 1 } log.Infof(ctx, "restarting node %d", pickedNode) if err := c.Restart(ctx, pickedNode); err != nil { t.Fatal(err) } CheckGossip(ctx, t, c, waitTime, HasPeers(num)) } }
func testFreezeClusterInner(t *testing.T, c cluster.Cluster, cfg cluster.TestConfig) { minAffected := int64(server.ExpectedInitialRangeCount()) const long = time.Minute const short = 10 * time.Second mustPost := func(freeze bool) serverpb.ClusterFreezeResponse { reply, err := postFreeze(c, freeze, long) if err != nil { t.Fatal(errors.Errorf("%v", err)) } return reply } if reply := mustPost(false); reply.RangesAffected != 0 { t.Fatalf("expected initial unfreeze to affect no ranges, got %d", reply.RangesAffected) } if reply := mustPost(true); reply.RangesAffected < minAffected { t.Fatalf("expected >=%d frozen ranges, got %d", minAffected, reply.RangesAffected) } if reply := mustPost(true); reply.RangesAffected != 0 { t.Fatalf("expected second freeze to affect no ranges, got %d", reply.RangesAffected) } if reply := mustPost(false); reply.RangesAffected < minAffected { t.Fatalf("expected >=%d thawed ranges, got %d", minAffected, reply.RangesAffected) } num := c.NumNodes() if num < 3 { t.Skip("skipping remainder of test; needs at least 3 nodes") } // Kill the last node. if err := c.Kill(num - 1); err != nil { t.Fatal(err) } // Attempt to freeze should get stuck (since it does not get confirmation // of the last node receiving the freeze command). // Note that this is the freeze trigger stalling on the Replica, not the // Store-polling mechanism. acceptErrs := strings.Join([]string{ "timed out waiting for Range", "Timeout exceeded while", "connection is closing", "deadline", // error returned via JSON when the server-side gRPC stream times out (due to // lack of new input). Unmarshaling that JSON fails with a message referencing // unknown fields, unfortunately in map order. "unknown field .*", }, "|") if reply, err := postFreeze(c, true, short); !testutils.IsError(err, acceptErrs) { t.Fatalf("expected timeout, got %v: %v", err, reply) } // Shut down the remaining nodes and restart them. for i := 0; i < num-1; i++ { if err := c.Kill(i); err != nil { t.Fatal(err) } } for i := 0; i < num; i++ { if err := c.Restart(i); err != nil { t.Fatal(err) } } // The cluster should now be fully operational (at least after waiting // a little bit) since each node tries to unfreeze everything when it // starts. if err := util.RetryForDuration(time.Minute, func() error { if _, err := postFreeze(c, false, short); err != nil { if testutils.IsError(err, "404 Not Found") { // It can take a bit until the endpoint is available. return err } t.Fatal(err) } // TODO(tschottdorf): moving the client creation outside of the retry // loop will break the test with the following message: // // client/rpc_sender.go:61: roachpb.Batch RPC failed as client // connection was closed // // Perhaps the cluster updates the address too late after restarting // the node. db, dbStopper := c.NewClient(t, 0) defer dbStopper.Stop() if _, err := db.Scan(context.TODO(), keys.LocalMax, roachpb.KeyMax, 0); err != nil { t.Fatal(err) } return nil }); err != nil { t.Fatal(err) } // Unfreezing again should be a no-op. if reply, err := postFreeze(c, false, long); err != nil { t.Fatal(err) } else if reply.RangesAffected > 0 { t.Fatalf("still %d frozen ranges", reply.RangesAffected) } }
// chaosMonkey picks a set of nodes and restarts them. If stopClients is set // all the clients are locked before the nodes are restarted. func chaosMonkey( ctx context.Context, state *testState, c cluster.Cluster, stopClients bool, pickNodes func() []int, consistentIdx int, ) { defer close(state.teardown) for curRound := uint64(1); !state.done(); curRound++ { atomic.StoreUint64(&state.monkeyIteration, curRound) select { case <-stopper.ShouldStop(): return default: } // Pick nodes to be restarted. nodes := pickNodes() if stopClients { // Prevent all clients from writing while nodes are being restarted. for i := 0; i < len(state.clients); i++ { state.clients[i].Lock() } } log.Infof(ctx, "round %d: restarting nodes %v", curRound, nodes) for _, i := range nodes { // Two early exit conditions. select { case <-stopper.ShouldStop(): break default: } if state.done() { break } log.Infof(ctx, "round %d: restarting %d", curRound, i) if err := c.Kill(ctx, i); err != nil { state.t.Error(err) } if err := c.Restart(ctx, i); err != nil { state.t.Error(err) } if stopClients { // Reinitialize the client talking to the restarted node. state.initClient(ctx, state.t, c, i) } } if stopClients { for i := 0; i < len(state.clients); i++ { state.clients[i].Unlock() } } preCount := state.counts() madeProgress := func() bool { newCounts := state.counts() for i := range newCounts { if newCounts[i] > preCount[i] { return true } } return false } // Sleep until at least one client is writing successfully. log.Warningf(ctx, "round %d: monkey sleeping while cluster recovers...", curRound) for !state.done() && !madeProgress() { time.Sleep(time.Second) } c.Assert(ctx, state.t) if err := cluster.Consistent(ctx, c, consistentIdx); err != nil { state.t.Error(err) } log.Warningf(ctx, "round %d: cluster recovered", curRound) } }
func testEventLogInner( ctx context.Context, t *testing.T, c cluster.Cluster, cfg cluster.TestConfig, ) { num := c.NumNodes() if num <= 0 { t.Fatalf("%d nodes in cluster", num) } var confirmedClusterID uuid.UUID type nodeEventInfo struct { Descriptor roachpb.NodeDescriptor ClusterID uuid.UUID } // Verify that a node_join message was logged for each node in the cluster. // We expect there to eventually be one such message for each node in the // cluster, and each message must be correctly formatted. util.SucceedsSoon(t, func() error { db := makePGClient(t, c.PGUrl(ctx, 0)) defer db.Close() // Query all node join events. There should be one for each node in the // cluster. rows, err := db.Query( "SELECT targetID, info FROM system.eventlog WHERE eventType = $1", string(csql.EventLogNodeJoin)) if err != nil { return err } seenIds := make(map[int64]struct{}) var clusterID uuid.UUID for rows.Next() { var targetID int64 var infoStr gosql.NullString if err := rows.Scan(&targetID, &infoStr); err != nil { t.Fatal(err) } // Verify the stored node descriptor. if !infoStr.Valid { t.Fatalf("info not recorded for node join, target node %d", targetID) } var info nodeEventInfo if err := json.Unmarshal([]byte(infoStr.String), &info); err != nil { t.Fatal(err) } if a, e := int64(info.Descriptor.NodeID), targetID; a != e { t.Fatalf("Node join with targetID %d had descriptor for wrong node %d", e, a) } // Verify cluster ID is recorded, and is the same for all nodes. if (info.ClusterID == uuid.UUID{}) { t.Fatalf("Node join recorded nil cluster id, info: %v", info) } if (clusterID == uuid.UUID{}) { clusterID = info.ClusterID } else if clusterID != info.ClusterID { t.Fatalf( "Node join recorded different cluster ID than earlier node. Expected %s, got %s. Info: %v", clusterID, info.ClusterID, info) } // Verify that all NodeIDs are different. if _, ok := seenIds[targetID]; ok { t.Fatalf("Node ID %d seen in two different node join messages", targetID) } seenIds[targetID] = struct{}{} } if err := rows.Err(); err != nil { return err } if a, e := len(seenIds), c.NumNodes(); a != e { return errors.Errorf("expected %d node join messages, found %d: %v", e, a, seenIds) } confirmedClusterID = clusterID return nil }) // Stop and Start Node 0, and verify the node restart message. if err := c.Kill(ctx, 0); err != nil { t.Fatal(err) } if err := c.Restart(ctx, 0); err != nil { t.Fatal(err) } util.SucceedsSoon(t, func() error { db := makePGClient(t, c.PGUrl(ctx, 0)) defer db.Close() // Query all node restart events. There should only be one. rows, err := db.Query( "SELECT targetID, info FROM system.eventlog WHERE eventType = $1", string(csql.EventLogNodeRestart)) if err != nil { return err } seenCount := 0 for rows.Next() { var targetID int64 var infoStr gosql.NullString if err := rows.Scan(&targetID, &infoStr); err != nil { t.Fatal(err) } // Verify the stored node descriptor. if !infoStr.Valid { t.Fatalf("info not recorded for node join, target node %d", targetID) } var info nodeEventInfo if err := json.Unmarshal([]byte(infoStr.String), &info); err != nil { t.Fatal(err) } if a, e := int64(info.Descriptor.NodeID), targetID; a != e { t.Fatalf("Node join with targetID %d had descriptor for wrong node %d", e, a) } // Verify cluster ID is recorded, and is the same for all nodes. if confirmedClusterID != info.ClusterID { t.Fatalf( "Node restart recorded different cluster ID than earlier join. Expected %s, got %s. Info: %v", confirmedClusterID, info.ClusterID, info) } seenCount++ } if err := rows.Err(); err != nil { return err } if seenCount != 1 { return errors.Errorf("Expected only one node restart event, found %d", seenCount) } return nil }) }
func testGossipRestartInner( ctx context.Context, t *testing.T, c cluster.Cluster, cfg cluster.TestConfig, ) { // This already replicates the first range (in the local setup). // The replication of the first range is important: as long as the // first range only exists on one node, that node can trivially // acquire the range lease. Once the range is replicated, however, // nodes must be able to discover each other over gossip before the // lease can be acquired. num := c.NumNodes() deadline := timeutil.Now().Add(cfg.Duration) waitTime := longWaitTime if cfg.Duration < waitTime { waitTime = shortWaitTime } for timeutil.Now().Before(deadline) { log.Infof(ctx, "waiting for initial gossip connections") CheckGossip(ctx, t, c, waitTime, HasPeers(num)) CheckGossip(ctx, t, c, waitTime, hasClusterID) CheckGossip(ctx, t, c, waitTime, hasSentinel) log.Infof(ctx, "killing all nodes") for i := 0; i < num; i++ { if err := c.Kill(ctx, i); err != nil { t.Fatal(err) } } log.Infof(ctx, "restarting all nodes") for i := 0; i < num; i++ { if err := c.Restart(ctx, i); err != nil { t.Fatal(err) } } log.Infof(ctx, "waiting for gossip to be connected") CheckGossip(ctx, t, c, waitTime, HasPeers(num)) CheckGossip(ctx, t, c, waitTime, hasClusterID) CheckGossip(ctx, t, c, waitTime, hasSentinel) for i := 0; i < num; i++ { db, err := c.NewClient(ctx, i) if err != nil { t.Fatal(err) } if i == 0 { if err := db.Del(ctx, "count"); err != nil { t.Fatal(err) } } var kv client.KeyValue if err := db.Txn(ctx, func(txn *client.Txn) error { var err error kv, err = txn.Inc("count", 1) return err }); err != nil { t.Fatal(err) } else if v := kv.ValueInt(); v != int64(i+1) { t.Fatalf("unexpected value %d for write #%d (expected %d)", v, i, i+1) } } } }