github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/gc/gc_random_test.go (about)

     1  // Copyright 2020 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 gc
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"math/rand"
    17  	"sort"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/cockroachdb/cockroach/pkg/config/zonepb"
    22  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    23  	"github.com/cockroachdb/cockroach/pkg/storage"
    24  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  // randomRunGCTestSpec specifies a distribution for to create random data for
    29  // testing Run
    30  type randomRunGCTestSpec struct {
    31  	ds  distSpec
    32  	now hlc.Timestamp
    33  	ttl int32 // seconds
    34  }
    35  
    36  var (
    37  	fewVersionsTinyRows = uniformDistSpec{
    38  		tsFrom: 0, tsTo: 100,
    39  		keySuffixMin: 2, keySuffixMax: 3,
    40  		valueLenMin: 1, valueLenMax: 1,
    41  		deleteFrac:      0,
    42  		keysPerValueMin: 1, keysPerValueMax: 2,
    43  		intentFrac: .1,
    44  	}
    45  	someVersionsMidSizeRows = uniformDistSpec{
    46  		tsFrom: 0, tsTo: 100,
    47  		keySuffixMin: 8, keySuffixMax: 8,
    48  		valueLenMin: 8, valueLenMax: 16,
    49  		deleteFrac:      .1,
    50  		keysPerValueMin: 1, keysPerValueMax: 100,
    51  		intentFrac: .1,
    52  	}
    53  	lotsOfVersionsMidSizeRows = uniformDistSpec{
    54  		tsFrom: 0, tsTo: 100,
    55  		keySuffixMin: 8, keySuffixMax: 8,
    56  		valueLenMin: 8, valueLenMax: 16,
    57  		deleteFrac:      .1,
    58  		keysPerValueMin: 1000, keysPerValueMax: 1000000,
    59  		intentFrac: .1,
    60  	}
    61  )
    62  
    63  // TestRunNewVsOld exercises the behavior of Run relative to the old
    64  // implementation. It runs both the new and old implementation and ensures
    65  // that they produce exactly the same results on the same set of keys.
    66  func TestRunNewVsOld(t *testing.T) {
    67  	rng := rand.New(rand.NewSource(1))
    68  	ctx := context.Background()
    69  	const N = 100000
    70  
    71  	someVersionsMidSizeRowsLotsOfIntents := someVersionsMidSizeRows
    72  	someVersionsMidSizeRowsLotsOfIntents.intentFrac = 1
    73  	for _, tc := range []randomRunGCTestSpec{
    74  		{
    75  			ds: someVersionsMidSizeRowsLotsOfIntents,
    76  			now: hlc.Timestamp{
    77  				WallTime: (IntentAgeThreshold + 100*time.Second).Nanoseconds(),
    78  			},
    79  			ttl: int32(IntentAgeThreshold.Seconds()),
    80  		},
    81  		{
    82  			ds: someVersionsMidSizeRows,
    83  			now: hlc.Timestamp{
    84  				WallTime: 100 * time.Second.Nanoseconds(),
    85  			},
    86  			ttl: 1,
    87  		},
    88  	} {
    89  		t.Run(fmt.Sprintf("%v@%v,ttl=%v", tc.ds, tc.now, tc.ttl), func(t *testing.T) {
    90  			eng := storage.NewDefaultInMem()
    91  			defer eng.Close()
    92  
    93  			tc.ds.dist(N, rng).setupTest(t, eng, *tc.ds.desc())
    94  			snap := eng.NewSnapshot()
    95  
    96  			oldGCer := makeFakeGCer()
    97  			policy := zonepb.GCPolicy{TTLSeconds: tc.ttl}
    98  			newThreshold := CalculateThreshold(tc.now, policy)
    99  			gcInfoOld, err := runGCOld(ctx, tc.ds.desc(), snap, tc.now,
   100  				newThreshold, policy,
   101  				&oldGCer,
   102  				oldGCer.resolveIntents,
   103  				oldGCer.resolveIntentsAsync)
   104  			require.NoError(t, err)
   105  
   106  			newGCer := makeFakeGCer()
   107  			gcInfoNew, err := Run(ctx, tc.ds.desc(), snap, tc.now,
   108  				newThreshold, policy,
   109  				&newGCer,
   110  				newGCer.resolveIntents,
   111  				newGCer.resolveIntentsAsync)
   112  			require.NoError(t, err)
   113  
   114  			oldGCer.normalize()
   115  			newGCer.normalize()
   116  			require.EqualValues(t, gcInfoOld, gcInfoNew)
   117  			require.EqualValues(t, oldGCer, newGCer)
   118  		})
   119  	}
   120  }
   121  
   122  // BenchmarkRun benchmarks the old and implementations of Run with different
   123  // data distributions.
   124  func BenchmarkRun(b *testing.B) {
   125  	rng := rand.New(rand.NewSource(1))
   126  	ctx := context.Background()
   127  	runGC := func(eng storage.Engine, old bool, spec randomRunGCTestSpec) (Info, error) {
   128  		runGCFunc := Run
   129  		if old {
   130  			runGCFunc = runGCOld
   131  		}
   132  		snap := eng.NewSnapshot()
   133  		policy := zonepb.GCPolicy{TTLSeconds: spec.ttl}
   134  		return runGCFunc(ctx, spec.ds.desc(), snap, spec.now,
   135  			CalculateThreshold(spec.now, policy),
   136  			policy,
   137  			NoopGCer{},
   138  			func(ctx context.Context, intents []roachpb.Intent) error {
   139  				return nil
   140  			},
   141  			func(ctx context.Context, txn *roachpb.Transaction, intents []roachpb.LockUpdate) error {
   142  				return nil
   143  			})
   144  	}
   145  	makeTest := func(old bool, spec randomRunGCTestSpec) func(b *testing.B) {
   146  		return func(b *testing.B) {
   147  			eng := storage.NewDefaultInMem()
   148  			defer eng.Close()
   149  			ms := spec.ds.dist(b.N, rng).setupTest(b, eng, *spec.ds.desc())
   150  			b.SetBytes(int64(float64(ms.Total()) / float64(b.N)))
   151  			b.ResetTimer()
   152  			_, err := runGC(eng, old, spec)
   153  			b.StopTimer()
   154  			require.NoError(b, err)
   155  		}
   156  	}
   157  	specsWithTTLs := func(
   158  		ds distSpec, now hlc.Timestamp, ttls []int32,
   159  	) (specs []randomRunGCTestSpec) {
   160  		for _, ttl := range ttls {
   161  			specs = append(specs, randomRunGCTestSpec{
   162  				ds:  ds,
   163  				now: now,
   164  				ttl: ttl,
   165  			})
   166  		}
   167  		return specs
   168  	}
   169  	ts100 := hlc.Timestamp{WallTime: (100 * time.Second).Nanoseconds()}
   170  	ttls := []int32{0, 25, 50, 75, 100}
   171  	specs := specsWithTTLs(fewVersionsTinyRows, ts100, ttls)
   172  	specs = append(specs, specsWithTTLs(someVersionsMidSizeRows, ts100, ttls)...)
   173  	specs = append(specs, specsWithTTLs(lotsOfVersionsMidSizeRows, ts100, ttls)...)
   174  	for _, old := range []bool{true, false} {
   175  		b.Run(fmt.Sprintf("old=%v", old), func(b *testing.B) {
   176  			for _, spec := range specs {
   177  				b.Run(fmt.Sprint(spec.ds), makeTest(old, spec))
   178  			}
   179  		})
   180  	}
   181  }
   182  
   183  type fakeGCer struct {
   184  	gcKeys     map[string]roachpb.GCRequest_GCKey
   185  	threshold  Threshold
   186  	intents    []roachpb.Intent
   187  	txnIntents []txnIntents
   188  }
   189  
   190  func makeFakeGCer() fakeGCer {
   191  	return fakeGCer{
   192  		gcKeys: make(map[string]roachpb.GCRequest_GCKey),
   193  	}
   194  }
   195  
   196  var _ GCer = (*fakeGCer)(nil)
   197  
   198  func (f *fakeGCer) SetGCThreshold(ctx context.Context, t Threshold) error {
   199  	f.threshold = t
   200  	return nil
   201  }
   202  
   203  func (f *fakeGCer) GC(ctx context.Context, keys []roachpb.GCRequest_GCKey) error {
   204  	for _, k := range keys {
   205  		f.gcKeys[k.Key.String()] = k
   206  	}
   207  	return nil
   208  }
   209  
   210  func (f *fakeGCer) resolveIntentsAsync(
   211  	_ context.Context, txn *roachpb.Transaction, intents []roachpb.LockUpdate,
   212  ) error {
   213  	f.txnIntents = append(f.txnIntents, txnIntents{txn: txn, intents: intents})
   214  	return nil
   215  }
   216  
   217  func (f *fakeGCer) resolveIntents(_ context.Context, intents []roachpb.Intent) error {
   218  	f.intents = append(f.intents, intents...)
   219  	return nil
   220  }
   221  
   222  func (f *fakeGCer) normalize() {
   223  	sortIntents := func(i, j int) bool {
   224  		return intentLess(&f.intents[i], &f.intents[j])
   225  	}
   226  	sort.Slice(f.intents, sortIntents)
   227  	for i := range f.txnIntents {
   228  		sort.Slice(f.txnIntents[i].intents, sortIntents)
   229  	}
   230  	sort.Slice(f.txnIntents, func(i, j int) bool {
   231  		return f.txnIntents[i].txn.ID.String() < f.txnIntents[j].txn.ID.String()
   232  	})
   233  }
   234  
   235  func intentLess(a, b *roachpb.Intent) bool {
   236  	cmp := a.Key.Compare(b.Key)
   237  	switch {
   238  	case cmp < 0:
   239  		return true
   240  	case cmp > 0:
   241  		return false
   242  	default:
   243  		return a.Txn.ID.String() < b.Txn.ID.String()
   244  	}
   245  }
   246  
   247  type txnIntents struct {
   248  	txn     *roachpb.Transaction
   249  	intents []roachpb.LockUpdate
   250  }