github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvclient/kvcoord/split_test.go (about) 1 // Copyright 2014 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 kvcoord 12 13 import ( 14 "context" 15 "math/rand" 16 "sync" 17 "sync/atomic" 18 "testing" 19 "time" 20 21 "github.com/cockroachdb/cockroach/pkg/config" 22 "github.com/cockroachdb/cockroach/pkg/config/zonepb" 23 "github.com/cockroachdb/cockroach/pkg/keys" 24 "github.com/cockroachdb/cockroach/pkg/kv" 25 "github.com/cockroachdb/cockroach/pkg/kv/kvserver" 26 "github.com/cockroachdb/cockroach/pkg/roachpb" 27 "github.com/cockroachdb/cockroach/pkg/storage" 28 "github.com/cockroachdb/cockroach/pkg/testutils" 29 "github.com/cockroachdb/cockroach/pkg/testutils/localtestcluster" 30 "github.com/cockroachdb/cockroach/pkg/util/hlc" 31 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 32 "github.com/cockroachdb/cockroach/pkg/util/log" 33 "github.com/cockroachdb/cockroach/pkg/util/randutil" 34 "github.com/cockroachdb/errors" 35 "github.com/gogo/protobuf/proto" 36 ) 37 38 // startTestWriter creates a writer which initiates a sequence of 39 // transactions, each which writes up to 10 times to random keys with 40 // random values. If not nil, txnChannel is written to non-blockingly 41 // every time a new transaction starts. 42 func startTestWriter( 43 db *kv.DB, 44 i int64, 45 valBytes int32, 46 wg *sync.WaitGroup, 47 retries *int32, 48 txnChannel chan struct{}, 49 done <-chan struct{}, 50 t *testing.T, 51 ) { 52 src := rand.New(rand.NewSource(i)) 53 defer func() { 54 if wg != nil { 55 wg.Done() 56 } 57 }() 58 59 for j := 0; ; j++ { 60 select { 61 case <-done: 62 return 63 default: 64 first := true 65 err := db.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 66 if first && txnChannel != nil { 67 select { 68 case txnChannel <- struct{}{}: 69 default: 70 } 71 } else if !first && retries != nil { 72 atomic.AddInt32(retries, 1) 73 } 74 first = false 75 for j := 0; j <= int(src.Int31n(10)); j++ { 76 key := randutil.RandBytes(src, 10) 77 val := randutil.RandBytes(src, int(src.Int31n(valBytes))) 78 if err := txn.Put(ctx, key, val); err != nil { 79 log.Infof(ctx, "experienced an error in routine %d: %s", i, err) 80 return err 81 } 82 } 83 return nil 84 }) 85 if err != nil { 86 t.Error(err) 87 } else { 88 time.Sleep(1 * time.Millisecond) 89 } 90 } 91 } 92 } 93 94 // TestRangeSplitMeta executes various splits (including at meta addressing) 95 // and checks that all created intents are resolved. This includes both intents 96 // which are resolved synchronously with EndTxn and via RPC. 97 func TestRangeSplitMeta(t *testing.T) { 98 defer leaktest.AfterTest(t)() 99 s := createTestDB(t) 100 defer s.Stop() 101 102 ctx := context.Background() 103 104 splitKeys := []roachpb.RKey{roachpb.RKey("G"), keys.RangeMetaKey(roachpb.RKey("F")), 105 keys.RangeMetaKey(roachpb.RKey("K")), keys.RangeMetaKey(roachpb.RKey("H"))} 106 107 // Execute the consecutive splits. 108 for _, splitRKey := range splitKeys { 109 splitKey := roachpb.Key(splitRKey) 110 log.Infof(ctx, "starting split at key %q...", splitKey) 111 if err := s.DB.AdminSplit(ctx, splitKey, splitKey, hlc.MaxTimestamp /* expirationTime */); err != nil { 112 t.Fatal(err) 113 } 114 log.Infof(ctx, "split at key %q complete", splitKey) 115 } 116 117 testutils.SucceedsSoon(t, func() error { 118 if _, err := storage.MVCCScan(ctx, s.Eng, keys.LocalMax, roachpb.KeyMax, hlc.MaxTimestamp, storage.MVCCScanOptions{}); err != nil { 119 return errors.Errorf("failed to verify no dangling intents: %s", err) 120 } 121 return nil 122 }) 123 } 124 125 // TestRangeSplitsWithConcurrentTxns does 5 consecutive splits while 126 // 10 concurrent goroutines are each running successive transactions 127 // composed of a random mix of puts. 128 func TestRangeSplitsWithConcurrentTxns(t *testing.T) { 129 defer leaktest.AfterTest(t)() 130 s := createTestDB(t) 131 defer s.Stop() 132 133 // This channel shuts the whole apparatus down. 134 done := make(chan struct{}) 135 txnChannel := make(chan struct{}, 1000) 136 137 // Set five split keys, about evenly spaced along the range of random keys. 138 splitKeys := []roachpb.Key{roachpb.Key("G"), roachpb.Key("R"), roachpb.Key("a"), roachpb.Key("l"), roachpb.Key("s")} 139 140 // Start up the concurrent goroutines which run transactions. 141 const concurrency = 10 142 var retries int32 143 var wg sync.WaitGroup 144 wg.Add(concurrency) 145 for i := 0; i < concurrency; i++ { 146 go startTestWriter(s.DB, int64(i), 1<<7, &wg, &retries, txnChannel, done, t) 147 } 148 149 ctx := context.Background() 150 // Execute the consecutive splits. 151 for _, splitKey := range splitKeys { 152 // Allow txns to start before initiating split. 153 for i := 0; i < concurrency; i++ { 154 <-txnChannel 155 } 156 log.Infof(ctx, "starting split at key %q...", splitKey) 157 if pErr := s.DB.AdminSplit(context.Background(), splitKey, splitKey, hlc.MaxTimestamp /* expirationTime */); pErr != nil { 158 t.Error(pErr) 159 } 160 log.Infof(ctx, "split at key %q complete", splitKey) 161 } 162 163 close(done) 164 wg.Wait() 165 166 if retries != 0 { 167 t.Errorf("expected no retries splitting a range with concurrent writes, "+ 168 "as range splits do not cause conflicts; got %d", retries) 169 } 170 } 171 172 // TestRangeSplitsWithWritePressure sets the zone config max bytes for 173 // a range to 256K and writes data until there are five ranges. 174 func TestRangeSplitsWithWritePressure(t *testing.T) { 175 defer leaktest.AfterTest(t)() 176 // Override default zone config. 177 cfg := zonepb.DefaultZoneConfigRef() 178 cfg.RangeMaxBytes = proto.Int64(1 << 18) 179 180 // Manually create the local test cluster so that the split queue 181 // is not disabled (LocalTestCluster disables it by default). 182 s := &localtestcluster.LocalTestCluster{ 183 Cfg: kvserver.StoreConfig{ 184 DefaultZoneConfig: cfg, 185 }, 186 StoreTestingKnobs: &kvserver.StoreTestingKnobs{ 187 DisableScanner: true, 188 }, 189 } 190 s.Start(t, testutils.NewNodeTestBaseContext(), InitFactoryForLocalTestCluster) 191 192 // This is purely to silence log spam. 193 config.TestingSetupZoneConfigHook(s.Stopper) 194 defer s.Stop() 195 196 // Start test writer write about a 32K/key so there aren't too many 197 // writes necessary to split 5 ranges. 198 done := make(chan struct{}) 199 var wg sync.WaitGroup 200 wg.Add(1) 201 go startTestWriter(s.DB, int64(0), 1<<15, &wg, nil, nil, done, t) 202 203 ctx := context.Background() 204 205 // Check that we split 5 times in allotted time. 206 testutils.SucceedsSoon(t, func() error { 207 // Scan the txn records. 208 rows, err := s.DB.Scan(ctx, keys.Meta2Prefix, keys.MetaMax, 0) 209 if err != nil { 210 return errors.Errorf("failed to scan meta2 keys: %s", err) 211 } 212 if lr := len(rows); lr < 5 { 213 return errors.Errorf("expected >= 5 scans; got %d", lr) 214 } 215 return nil 216 }) 217 close(done) 218 wg.Wait() 219 220 // This write pressure test often causes splits while resolve 221 // intents are in flight, causing them to fail with range key 222 // mismatch errors. However, LocalSender should retry in these 223 // cases. Check here via MVCC scan that there are no dangling write 224 // intents. We do this using a SucceedsSoon construct to account 225 // for timing of finishing the test writer and a possibly-ongoing 226 // asynchronous split. 227 testutils.SucceedsSoon(t, func() error { 228 if _, err := storage.MVCCScan(ctx, s.Eng, keys.LocalMax, roachpb.KeyMax, hlc.MaxTimestamp, storage.MVCCScanOptions{}); err != nil { 229 return errors.Errorf("failed to verify no dangling intents: %s", err) 230 } 231 return nil 232 }) 233 } 234 235 // TestRangeSplitsWithSameKeyTwice check that second range split 236 // on the same splitKey succeeds. 237 func TestRangeSplitsWithSameKeyTwice(t *testing.T) { 238 defer leaktest.AfterTest(t)() 239 s := createTestDBWithContextAndKnobs(t, kv.DefaultDBContext(), &kvserver.StoreTestingKnobs{ 240 DisableScanner: true, 241 DisableSplitQueue: true, 242 DisableMergeQueue: true, 243 }) 244 defer s.Stop() 245 246 ctx := context.Background() 247 248 splitKey := roachpb.Key("aa") 249 log.Infof(ctx, "starting split at key %q...", splitKey) 250 if err := s.DB.AdminSplit(ctx, splitKey, splitKey, hlc.MaxTimestamp /* expirationTime */); err != nil { 251 t.Fatal(err) 252 } 253 log.Infof(ctx, "split at key %q first time complete", splitKey) 254 if err := s.DB.AdminSplit(ctx, splitKey, splitKey, hlc.MaxTimestamp /* expirationTime */); err != nil { 255 t.Fatal(err) 256 } 257 } 258 259 // TestSplitStickyBit checks that the sticky bit is set when performing a manual 260 // split. There are two cases to consider: 261 // 1. Range is split so sticky bit is updated on RHS. 262 // 2. Range is already split and split key is the start key of a range, so update 263 // the sticky bit of that range, but no range is split. 264 func TestRangeSplitsStickyBit(t *testing.T) { 265 defer leaktest.AfterTest(t)() 266 s := createTestDBWithContextAndKnobs(t, kv.DefaultDBContext(), &kvserver.StoreTestingKnobs{ 267 DisableScanner: true, 268 DisableSplitQueue: true, 269 DisableMergeQueue: true, 270 }) 271 defer s.Stop() 272 273 ctx := context.Background() 274 splitKey := roachpb.RKey("aa") 275 descKey := keys.RangeDescriptorKey(splitKey) 276 277 // Splitting range. 278 if err := s.DB.AdminSplit(ctx, splitKey.AsRawKey(), splitKey.AsRawKey(), hlc.MaxTimestamp /* expirationTime */); err != nil { 279 t.Fatal(err) 280 } 281 282 // Checking sticky bit. 283 var desc roachpb.RangeDescriptor 284 err := s.DB.GetProto(ctx, descKey, &desc) 285 if err != nil { 286 t.Fatal(err) 287 } 288 if (desc.GetStickyBit() == hlc.Timestamp{}) { 289 t.Fatal("Sticky bit not set after splitting") 290 } 291 292 // Removing sticky bit. 293 if err := s.DB.AdminUnsplit(ctx, splitKey.AsRawKey()); err != nil { 294 t.Fatal(err) 295 } 296 297 // Splitting range. 298 if err := s.DB.AdminSplit(ctx, splitKey.AsRawKey(), splitKey.AsRawKey(), hlc.MaxTimestamp /* expirationTime */); err != nil { 299 t.Fatal(err) 300 } 301 302 // Checking sticky bit. 303 err = s.DB.GetProto(ctx, descKey, &desc) 304 if err != nil { 305 t.Fatal(err) 306 } 307 if (desc.GetStickyBit() == hlc.Timestamp{}) { 308 t.Fatal("Sticky bit not set after splitting") 309 } 310 }