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  }