github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/server/intent_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 server 12 13 import ( 14 "context" 15 "fmt" 16 "reflect" 17 "sort" 18 "testing" 19 20 "github.com/cockroachdb/cockroach/pkg/base" 21 "github.com/cockroachdb/cockroach/pkg/kv" 22 "github.com/cockroachdb/cockroach/pkg/kv/kvserver" 23 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase" 24 "github.com/cockroachdb/cockroach/pkg/roachpb" 25 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 26 "github.com/cockroachdb/cockroach/pkg/util/hlc" 27 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 28 "github.com/cockroachdb/cockroach/pkg/util/log" 29 "github.com/cockroachdb/cockroach/pkg/util/randutil" 30 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 31 ) 32 33 // TODO(benesch): move this test to somewhere more specific than package server. 34 func TestIntentResolution(t *testing.T) { 35 defer leaktest.AfterTest(t)() 36 37 testCases := []struct { 38 keys []string 39 ranges [][2]string 40 exp []string 41 }{ 42 // Note that the first key (or, range, if no keys present) determines 43 // the base key of the Txn. In these examples, it's always the first 44 // range, so "a"-"s" is local. Any examples added must stick to that 45 // convention and write the first key into "a"-"s". 46 47 { 48 keys: []string{"a", "x", "b", "c", "s"}, 49 ranges: [][2]string{{"d", "e"}}, 50 exp: []string{"s", "x"}, 51 }, 52 { 53 keys: []string{"h", "y", "z"}, 54 ranges: [][2]string{{"g", "z"}}, 55 exp: []string{`"s"-"z\x00"`}, 56 }, 57 { 58 keys: []string{"q", "s"}, 59 ranges: [][2]string{{"a", "w"}, {"b", "x"}, {"t", "u"}}, 60 exp: []string{`"s"-"x"`}, 61 }, 62 { 63 keys: []string{"q", "s", "y", "v"}, 64 ranges: [][2]string{{"a", "s"}, {"r", "t"}, {"u", "w"}}, 65 exp: []string{`"s"-"t"`, `"u"-"w"`, "y"}, 66 }, 67 } 68 69 splitKey := []byte("s") 70 for i, tc := range testCases { 71 // Use deterministic randomness to randomly put the writes in separate 72 // batches or commit them with EndTxn. 73 rnd, seed := randutil.NewPseudoRand() 74 log.Infof(context.Background(), "%d: using intent test seed %d", i, seed) 75 76 results := map[string]struct{}{} 77 func() { 78 var storeKnobs kvserver.StoreTestingKnobs 79 var mu syncutil.Mutex 80 closer := make(chan struct{}, 2) 81 var done bool 82 storeKnobs.EvalKnobs.TestingEvalFilter = 83 func(filterArgs kvserverbase.FilterArgs) *roachpb.Error { 84 mu.Lock() 85 defer mu.Unlock() 86 header := filterArgs.Req.Header() 87 // Ignore anything outside of the intent key range of "a" - "z" 88 if header.Key.Compare(roachpb.Key("a")) < 0 || header.Key.Compare(roachpb.Key("z")) > 0 { 89 return nil 90 } 91 var entry string 92 switch arg := filterArgs.Req.(type) { 93 case *roachpb.ResolveIntentRequest: 94 if arg.Status == roachpb.COMMITTED { 95 entry = string(header.Key) 96 } 97 case *roachpb.ResolveIntentRangeRequest: 98 if arg.Status == roachpb.COMMITTED { 99 entry = fmt.Sprintf("%s-%s", header.Key, header.EndKey) 100 } 101 } 102 if entry != "" { 103 log.Infof(context.Background(), "got %s", entry) 104 results[entry] = struct{}{} 105 } 106 if len(results) >= len(tc.exp) && !done { 107 done = true 108 close(closer) 109 } 110 return nil 111 } 112 113 // TODO(benesch): starting a test server for every test case is needlessly 114 // inefficient. 115 s, _, kvDB := serverutils.StartServer(t, base.TestServerArgs{ 116 Knobs: base.TestingKnobs{Store: &storeKnobs}}) 117 defer s.Stopper().Stop(context.Background()) 118 // Split the Range. This should not have any asynchronous intents. 119 if err := kvDB.AdminSplit(context.Background(), splitKey, splitKey, hlc.MaxTimestamp /* expirationTime */); err != nil { 120 t.Fatal(err) 121 } 122 123 if err := kvDB.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 124 b := txn.NewBatch() 125 if tc.keys[0] >= string(splitKey) { 126 t.Fatalf("first key %s must be < split key %s", tc.keys[0], splitKey) 127 } 128 for i, key := range tc.keys { 129 // The first write must not go to batch, it anchors the 130 // transaction to the correct range. 131 local := i != 0 && rnd.Intn(2) == 0 132 log.Infof(context.Background(), "%d: %s: local: %t", i, key, local) 133 if local { 134 b.Put(key, "test") 135 } else if err := txn.Put(ctx, key, "test"); err != nil { 136 return err 137 } 138 } 139 140 for _, kr := range tc.ranges { 141 local := rnd.Intn(2) == 0 142 log.Infof(context.Background(), "%d: [%s,%s): local: %t", i, kr[0], kr[1], local) 143 if local { 144 b.DelRange(kr[0], kr[1], false) 145 } else if err := txn.DelRange(ctx, kr[0], kr[1]); err != nil { 146 return err 147 } 148 } 149 150 return txn.CommitInBatch(ctx, b) 151 }); err != nil { 152 t.Fatalf("%d: %s", i, err) 153 } 154 <-closer // wait for async intents 155 // Use Raft to make it likely that any straddling intent 156 // resolutions have come in. Don't touch existing data; that could 157 // generate unexpected intent resolutions. 158 if _, err := kvDB.Scan(context.Background(), "z\x00", "z\x00\x00", 0); err != nil { 159 t.Fatal(err) 160 } 161 }() 162 // Verification. Note that this runs after the system has stopped, so that 163 // everything asynchronous has already happened. 164 expResult := tc.exp 165 sort.Strings(expResult) 166 var actResult []string 167 for k := range results { 168 actResult = append(actResult, k) 169 } 170 sort.Strings(actResult) 171 if !reflect.DeepEqual(actResult, expResult) { 172 t.Fatalf("%d: unexpected non-local intents, expected %s: %s", i, expResult, actResult) 173 } 174 } 175 }