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 }