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  }