@@ -12,14 +12,19 @@ import (
1212 "cmp"
1313 "context"
1414 "fmt"
15+ "strconv"
1516 "strings"
1617 "testing"
1718 "time"
1819
20+ "github.com/cockroachdb/cockroach/pkg/keys"
21+ "github.com/cockroachdb/cockroach/pkg/kv/kvpb"
1922 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/abortspan"
2023 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval"
24+ "github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock"
2125 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb"
2226 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvstorage"
27+ "github.com/cockroachdb/cockroach/pkg/kv/kvserver/logstore"
2328 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/print"
2429 "github.com/cockroachdb/cockroach/pkg/raft/raftpb"
2530 "github.com/cockroachdb/cockroach/pkg/roachpb"
@@ -31,6 +36,7 @@ import (
3136 "github.com/cockroachdb/cockroach/pkg/util/leaktest"
3237 "github.com/cockroachdb/cockroach/pkg/util/log"
3338 "github.com/cockroachdb/cockroach/pkg/util/timeutil"
39+ "github.com/cockroachdb/cockroach/pkg/util/uuid"
3440 "github.com/cockroachdb/datadriven"
3541 "github.com/cockroachdb/errors"
3642 "github.com/stretchr/testify/require"
@@ -87,6 +93,20 @@ import (
8793// Destroys the replica on n1 for the specified range. The replica's state
8894// must have already been created via create-replica.
8995//
96+ // append-raft-entries range-id=<int> num-entries=<int>
97+ // ----
98+ //
99+ // Appends the specified number of dummy raft entries to the raft log for
100+ // the replica on n1/s1. The replica must have already been created via
101+ // create-replica.
102+ //
103+ // create-range-data range-id=<int> [num-user-keys=<int>] [num-system-keys=<int>] [num-lock-table-keys=<int>]
104+ // ----
105+ //
106+ // Creates the specified number of user, system, and lock table keys for the
107+ // range. At least one parameter should be non-zero to ensure this directive is
108+ // not nonsensical.
109+ //
90110// print-range-state [sort-keys=<bool>]
91111// ----
92112//
@@ -97,6 +117,8 @@ func TestReplicaLifecycleDataDriven(t *testing.T) {
97117 defer leaktest .AfterTest (t )()
98118 defer log .Scope (t ).Close (t )
99119
120+ storage .DisableMetamorphicSimpleValueEncoding (t ) // for deterministic output
121+
100122 datadriven .Walk (t , "testdata/replica_lifecycle" , func (t * testing.T , path string ) {
101123 tc := newTestCtx ()
102124 defer tc .close ()
@@ -303,7 +325,7 @@ func TestReplicaLifecycleDataDriven(t *testing.T) {
303325 kvstorage .TODOReadWriter (batch ),
304326 kvstorage.DestroyReplicaInfo {
305327 FullReplicaID : rs .replica .FullReplicaID ,
306- Keys : roachpb. RSpan { Key : rs .desc .StartKey , EndKey : rs . desc . EndKey } ,
328+ Keys : rs .desc .RSpan () ,
307329 },
308330 rs .desc .NextReplicaID ,
309331 )
@@ -317,6 +339,80 @@ func TestReplicaLifecycleDataDriven(t *testing.T) {
317339 rs .replica = nil
318340 return output
319341
342+ case "append-raft-entries" :
343+ rangeID := dd .ScanArg [roachpb.RangeID ](t , d , "range-id" )
344+ numEntries := dd .ScanArg [int ](t , d , "num-entries" )
345+ rs := tc .mustGetRangeState (t , rangeID )
346+ require .NotNil (t , rs .replica , "replica must be created before appending entries" )
347+
348+ batch := tc .storage .NewBatch ()
349+ defer batch .Close ()
350+
351+ sl := logstore .NewStateLoader (rangeID )
352+ lastIndex := rs .replica .lastIdx
353+ rs .replica .lastIdx += kvpb .RaftIndex (numEntries )
354+ term := rs .replica .hs .Term
355+
356+ for i := 0 ; i < numEntries ; i ++ {
357+ entryIndex := lastIndex + 1 + kvpb .RaftIndex (i )
358+ require .NoError (t , storage .MVCCBlindPutProto (
359+ ctx , batch ,
360+ sl .RaftLogKey (entryIndex ), hlc.Timestamp {},
361+ & raftpb.Entry {Index : uint64 (entryIndex ), Term : term },
362+ storage.MVCCWriteOptions {},
363+ ))
364+ }
365+
366+ output , err := print .DecodeWriteBatch (batch .Repr ())
367+ require .NoError (t , err )
368+ err = batch .Commit (true )
369+ require .NoError (t , err )
370+ return strings .ReplaceAll (output , "\n \n " , "\n " )
371+
372+ case "create-range-data" :
373+ rangeID := dd .ScanArg [roachpb.RangeID ](t , d , "range-id" )
374+ numUserKeys := dd .ScanArgOr (t , d , "num-user-keys" , 0 )
375+ numSystemKeys := dd .ScanArgOr (t , d , "num-system-keys" , 0 )
376+ numLockTableKeys := dd .ScanArgOr (t , d , "num-lock-table-keys" , 0 )
377+ require .True (t , numUserKeys > 0 || numSystemKeys > 0 || numLockTableKeys > 0 )
378+
379+ rs := tc .mustGetRangeState (t , rangeID )
380+ batch := tc .storage .NewBatch ()
381+ defer batch .Close ()
382+
383+ ts := hlc.Timestamp {WallTime : 1 }
384+
385+ getUserKey := func (i int ) roachpb.Key {
386+ return append (rs .desc .StartKey .AsRawKey (), strconv .Itoa (i )... )
387+ }
388+
389+ // 1. User keys.
390+ for i := 0 ; i < numUserKeys ; i ++ {
391+ require .NoError (t , batch .PutMVCC (
392+ storage.MVCCKey {Key : getUserKey (i ), Timestamp : ts }, storage.MVCCValue {},
393+ ))
394+ }
395+ // 2. System keys.
396+ for i := 0 ; i < numSystemKeys ; i ++ {
397+ key := keys .TransactionKey (getUserKey (i ), uuid .NamespaceDNS )
398+ require .NoError (t , batch .PutMVCC (
399+ storage.MVCCKey {Key : key , Timestamp : ts }, storage.MVCCValue {},
400+ ))
401+ }
402+ // 3. Lock table keys.
403+ for i := 0 ; i < numLockTableKeys ; i ++ {
404+ ek , _ := storage.LockTableKey {
405+ Key : getUserKey (i ), Strength : lock .Intent , TxnUUID : uuid.UUID {},
406+ }.ToEngineKey (nil )
407+ require .NoError (t , batch .PutEngineKey (ek , nil ))
408+ }
409+
410+ output , err := print .DecodeWriteBatch (batch .Repr ())
411+ require .NoError (t , err )
412+ err = batch .Commit (true )
413+ require .NoError (t , err )
414+ return output
415+
320416 case "print-range-state" :
321417 var sb strings.Builder
322418 if len (tc .ranges ) == 0 {
@@ -363,8 +459,9 @@ type rangeState struct {
363459// engine (both raft log and state machine) state.
364460type replicaInfo struct {
365461 roachpb.FullReplicaID
366- hs raftpb.HardState
367- ts kvserverpb.RaftTruncatedState
462+ hs raftpb.HardState
463+ ts kvserverpb.RaftTruncatedState
464+ lastIdx kvpb.RaftIndex
368465}
369466
370467// testCtx is a single test's context. It tracks the state of all ranges and any
@@ -442,8 +539,9 @@ func (tc *testCtx) updatePostReplicaCreateState(
442539 RangeID : rs .desc .RangeID ,
443540 ReplicaID : replID .ReplicaID ,
444541 },
445- hs : hs ,
446- ts : ts ,
542+ hs : hs ,
543+ ts : ts ,
544+ lastIdx : ts .Index ,
447545 }
448546}
449547
@@ -504,8 +602,9 @@ func (r *replicaInfo) String() string {
504602 if r .hs == (raftpb.HardState {}) {
505603 sb .WriteString ("uninitialized" )
506604 } else {
507- sb .WriteString (fmt .Sprintf ("HardState={Term:%d,Vote:%d,Commit:%d} " , r .hs .Term , r .hs .Vote , r .hs .Commit ))
508- sb .WriteString (fmt .Sprintf ("TruncatedState={Index:%d,Term:%d}" , r .ts .Index , r .ts .Term ))
605+ sb .WriteString (fmt .Sprintf ("HardState={Term:%d,Vote:%d,Commit:%d}" , r .hs .Term , r .hs .Vote , r .hs .Commit ))
606+ sb .WriteString (fmt .Sprintf (" TruncatedState={Index:%d,Term:%d}" , r .ts .Index , r .ts .Term ))
607+ sb .WriteString (fmt .Sprintf (" LastIdx=%d" , r .lastIdx ))
509608 }
510609 return sb .String ()
511610}
0 commit comments