github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/replica_application_state_machine_test.go (about) 1 // Copyright 2019 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 "testing" 16 17 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/apply" 18 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb" 19 "github.com/cockroachdb/cockroach/pkg/roachpb" 20 "github.com/cockroachdb/cockroach/pkg/testutils" 21 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 22 "github.com/cockroachdb/cockroach/pkg/util/stop" 23 "github.com/stretchr/testify/require" 24 "go.etcd.io/etcd/raft/raftpb" 25 ) 26 27 // TestReplicaStateMachineChangeReplicas tests the behavior of applying a 28 // replicated command with a ChangeReplicas trigger in a replicaAppBatch. 29 // The test exercises the logic of applying both old-style and new-style 30 // ChangeReplicas triggers. 31 func TestReplicaStateMachineChangeReplicas(t *testing.T) { 32 defer leaktest.AfterTest(t)() 33 testutils.RunTrueAndFalse(t, "add replica", func(t *testing.T, add bool) { 34 testutils.RunTrueAndFalse(t, "deprecated", func(t *testing.T, deprecated bool) { 35 tc := testContext{} 36 ctx := context.Background() 37 stopper := stop.NewStopper() 38 defer stopper.Stop(ctx) 39 tc.Start(t, stopper) 40 41 // Lock the replica for the entire test. 42 r := tc.repl 43 r.raftMu.Lock() 44 defer r.raftMu.Unlock() 45 sm := r.getStateMachine() 46 47 desc := r.Desc() 48 replDesc, ok := desc.GetReplicaDescriptor(r.store.StoreID()) 49 require.True(t, ok) 50 51 newDesc := *desc 52 newDesc.InternalReplicas = append([]roachpb.ReplicaDescriptor(nil), desc.InternalReplicas...) 53 var trigger roachpb.ChangeReplicasTrigger 54 var confChange raftpb.ConfChange 55 if add { 56 // Add a new replica to the Range. 57 addedReplDesc := newDesc.AddReplica(replDesc.NodeID+1, replDesc.StoreID+1, roachpb.VOTER_FULL) 58 59 if deprecated { 60 trigger = roachpb.ChangeReplicasTrigger{ 61 DeprecatedChangeType: roachpb.ADD_REPLICA, 62 DeprecatedReplica: addedReplDesc, 63 DeprecatedUpdatedReplicas: []roachpb.ReplicaDescriptor{ 64 replDesc, 65 addedReplDesc, 66 }, 67 DeprecatedNextReplicaID: addedReplDesc.ReplicaID + 1, 68 } 69 } else { 70 trigger = roachpb.ChangeReplicasTrigger{ 71 Desc: &newDesc, 72 InternalAddedReplicas: []roachpb.ReplicaDescriptor{addedReplDesc}, 73 } 74 } 75 76 confChange = raftpb.ConfChange{ 77 Type: raftpb.ConfChangeAddNode, 78 NodeID: uint64(addedReplDesc.NodeID), 79 } 80 } else { 81 // Remove ourselves from the Range. 82 removedReplDesc, ok := newDesc.RemoveReplica(replDesc.NodeID, replDesc.StoreID) 83 require.True(t, ok) 84 85 if deprecated { 86 trigger = roachpb.ChangeReplicasTrigger{ 87 DeprecatedChangeType: roachpb.REMOVE_REPLICA, 88 DeprecatedReplica: removedReplDesc, 89 DeprecatedUpdatedReplicas: []roachpb.ReplicaDescriptor{}, 90 DeprecatedNextReplicaID: replDesc.ReplicaID + 1, 91 } 92 } else { 93 trigger = roachpb.ChangeReplicasTrigger{ 94 Desc: &newDesc, 95 InternalRemovedReplicas: []roachpb.ReplicaDescriptor{removedReplDesc}, 96 } 97 } 98 99 confChange = raftpb.ConfChange{ 100 Type: raftpb.ConfChangeRemoveNode, 101 NodeID: uint64(removedReplDesc.NodeID), 102 } 103 } 104 105 // Create a new application batch. 106 b := sm.NewBatch(false /* ephemeral */).(*replicaAppBatch) 107 defer b.Close() 108 109 // Stage a command with the ChangeReplicas trigger. 110 cmd := &replicatedCmd{ 111 ctx: ctx, 112 ent: &raftpb.Entry{ 113 Index: r.mu.state.RaftAppliedIndex + 1, 114 Type: raftpb.EntryConfChange, 115 }, 116 decodedRaftEntry: decodedRaftEntry{ 117 idKey: makeIDKey(), 118 raftCmd: kvserverpb.RaftCommand{ 119 ProposerLeaseSequence: r.mu.state.Lease.Sequence, 120 MaxLeaseIndex: r.mu.state.LeaseAppliedIndex + 1, 121 ReplicatedEvalResult: kvserverpb.ReplicatedEvalResult{ 122 State: &kvserverpb.ReplicaState{Desc: &newDesc}, 123 ChangeReplicas: &kvserverpb.ChangeReplicas{ChangeReplicasTrigger: trigger}, 124 Timestamp: r.mu.state.GCThreshold.Add(1, 0), 125 }, 126 }, 127 confChange: &decodedConfChange{ 128 ConfChangeI: confChange, 129 }, 130 }, 131 } 132 133 checkedCmd, err := b.Stage(cmd) 134 require.NoError(t, err) 135 require.Equal(t, !add, b.changeRemovesReplica) 136 require.Equal(t, b.state.RaftAppliedIndex, cmd.ent.Index) 137 require.Equal(t, b.state.LeaseAppliedIndex, cmd.raftCmd.MaxLeaseIndex) 138 139 // Check the replica's destroy status. 140 reason, _ := r.IsDestroyed() 141 if add { 142 require.Equal(t, destroyReasonAlive, reason) 143 } else { 144 require.Equal(t, destroyReasonRemoved, reason) 145 } 146 147 // Apply the batch to the StateMachine. 148 err = b.ApplyToStateMachine(ctx) 149 require.NoError(t, err) 150 151 // Apply the side effects of the command to the StateMachine. 152 _, err = sm.ApplySideEffects(checkedCmd) 153 if add { 154 require.NoError(t, err) 155 } else { 156 require.Equal(t, apply.ErrRemoved, err) 157 } 158 159 // Check whether the Replica still exists in the Store. 160 _, err = tc.store.GetReplica(r.RangeID) 161 if add { 162 require.NoError(t, err) 163 } else { 164 require.Error(t, err) 165 require.IsType(t, &roachpb.RangeNotFoundError{}, err) 166 } 167 }) 168 }) 169 }