github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/cmd_truncate_log_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 batcheval 12 13 import ( 14 "context" 15 "fmt" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/keys" 19 "github.com/cockroachdb/cockroach/pkg/roachpb" 20 "github.com/cockroachdb/cockroach/pkg/storage" 21 "github.com/cockroachdb/cockroach/pkg/util/hlc" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 "github.com/stretchr/testify/assert" 24 ) 25 26 func putTruncatedState( 27 t *testing.T, 28 eng storage.Engine, 29 rangeID roachpb.RangeID, 30 truncState roachpb.RaftTruncatedState, 31 legacy bool, 32 ) { 33 key := keys.RaftTruncatedStateKey(rangeID) 34 if legacy { 35 key = keys.RaftTruncatedStateLegacyKey(rangeID) 36 } 37 if err := storage.MVCCPutProto( 38 context.Background(), eng, nil, key, 39 hlc.Timestamp{}, nil /* txn */, &truncState, 40 ); err != nil { 41 t.Fatal(err) 42 } 43 } 44 45 func readTruncStates( 46 t *testing.T, eng storage.Engine, rangeID roachpb.RangeID, 47 ) (legacy roachpb.RaftTruncatedState, unreplicated roachpb.RaftTruncatedState) { 48 t.Helper() 49 legacyFound, err := storage.MVCCGetProto( 50 context.Background(), eng, keys.RaftTruncatedStateLegacyKey(rangeID), 51 hlc.Timestamp{}, &legacy, storage.MVCCGetOptions{}, 52 ) 53 if err != nil { 54 t.Fatal(err) 55 } 56 if legacyFound != (legacy != roachpb.RaftTruncatedState{}) { 57 t.Fatalf("legacy key found=%t but state is %+v", legacyFound, legacy) 58 } 59 60 unreplicatedFound, err := storage.MVCCGetProto( 61 context.Background(), eng, keys.RaftTruncatedStateKey(rangeID), 62 hlc.Timestamp{}, &unreplicated, storage.MVCCGetOptions{}, 63 ) 64 if err != nil { 65 t.Fatal(err) 66 } 67 if unreplicatedFound != (unreplicated != roachpb.RaftTruncatedState{}) { 68 t.Fatalf("unreplicated key found=%t but state is %+v", unreplicatedFound, unreplicated) 69 } 70 return 71 } 72 73 const ( 74 expectationNeither = iota 75 expectationLegacy 76 expectationUnreplicated 77 ) 78 79 type unreplicatedTruncStateTest struct { 80 startsWithLegacy bool 81 exp int // see consts above 82 } 83 84 func TestTruncateLogUnreplicatedTruncatedState(t *testing.T) { 85 defer leaktest.AfterTest(t)() 86 87 // Check out the old clusterversion.VersionUnreplicatedRaftTruncatedState 88 // for information on what's being tested. The cluster version is gone, but 89 // the migration is done range by range and so it still exists. 90 91 const ( 92 startsLegacy = true 93 startsUnreplicated = false 94 ) 95 96 testCases := []unreplicatedTruncStateTest{ 97 // This is the case where we've already migrated. 98 {startsUnreplicated, expectationUnreplicated}, 99 // This is the case in which the migration is triggered. As a result, 100 // we see neither of the keys written. The new key will be written 101 // atomically as a side effect (outside of the scope of this test). 102 {startsLegacy, expectationNeither}, 103 } 104 105 for _, tc := range testCases { 106 t.Run(fmt.Sprintf("%v", tc), func(t *testing.T) { 107 runUnreplicatedTruncatedState(t, tc) 108 }) 109 } 110 } 111 112 func runUnreplicatedTruncatedState(t *testing.T, tc unreplicatedTruncStateTest) { 113 ctx := context.Background() 114 115 const ( 116 rangeID = 12 117 term = 10 118 firstIndex = 100 119 ) 120 121 evalCtx := &MockEvalCtx{ 122 Desc: &roachpb.RangeDescriptor{RangeID: rangeID}, 123 Term: term, 124 FirstIndex: firstIndex, 125 } 126 127 eng := storage.NewDefaultInMem() 128 defer eng.Close() 129 130 truncState := roachpb.RaftTruncatedState{ 131 Index: firstIndex + 1, 132 Term: term, 133 } 134 135 // Put down the TruncatedState specified by the test case. 136 putTruncatedState(t, eng, rangeID, truncState, tc.startsWithLegacy) 137 138 // Send a truncation request. 139 req := roachpb.TruncateLogRequest{ 140 RangeID: rangeID, 141 Index: firstIndex + 7, 142 } 143 cArgs := CommandArgs{ 144 EvalCtx: evalCtx.EvalContext(), 145 Args: &req, 146 } 147 resp := &roachpb.TruncateLogResponse{} 148 res, err := TruncateLog(ctx, eng, cArgs, resp) 149 if err != nil { 150 t.Fatal(err) 151 } 152 153 expTruncState := roachpb.RaftTruncatedState{ 154 Index: req.Index - 1, 155 Term: term, 156 } 157 158 legacy, unreplicated := readTruncStates(t, eng, rangeID) 159 160 switch tc.exp { 161 case expectationLegacy: 162 assert.Equal(t, expTruncState, legacy) 163 assert.Zero(t, unreplicated) 164 case expectationUnreplicated: 165 // The unreplicated key that we see should be the initial truncated 166 // state (it's only updated below Raft). 167 assert.Equal(t, truncState, unreplicated) 168 assert.Zero(t, legacy) 169 case expectationNeither: 170 assert.Zero(t, unreplicated) 171 assert.Zero(t, legacy) 172 default: 173 t.Fatalf("unknown expectation %d", tc.exp) 174 } 175 176 assert.NotNil(t, res.Replicated.State) 177 assert.NotNil(t, res.Replicated.State.TruncatedState) 178 assert.Equal(t, expTruncState, *res.Replicated.State.TruncatedState) 179 }