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  }