github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/pebble_test.go (about)

     1  // Copyright 2019 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 storage
    12  
    13  import (
    14  	"bytes"
    15  	"fmt"
    16  	"io/ioutil"
    17  	"math/rand"
    18  	"sort"
    19  	"strconv"
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    24  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    25  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    26  	"github.com/cockroachdb/cockroach/pkg/util/protoutil"
    27  	"github.com/cockroachdb/cockroach/pkg/util/randutil"
    28  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    29  	"github.com/cockroachdb/datadriven"
    30  	"github.com/cockroachdb/pebble"
    31  )
    32  
    33  func TestPebbleTimeBoundPropCollector(t *testing.T) {
    34  	defer leaktest.AfterTest(t)()
    35  
    36  	datadriven.RunTest(t, "testdata/time_bound_props", func(t *testing.T, d *datadriven.TestData) string {
    37  		c := &pebbleTimeBoundPropCollector{}
    38  		switch d.Cmd {
    39  		case "build":
    40  			for _, line := range strings.Split(d.Input, "\n") {
    41  				parts := strings.Fields(line)
    42  				if len(parts) != 2 {
    43  					return fmt.Sprintf("malformed line: %s, expected: <key>/<timestamp> <value>", line)
    44  				}
    45  				keyParts := strings.Split(parts[0], "/")
    46  				if len(keyParts) != 2 {
    47  					return fmt.Sprintf("malformed key: %s, expected: <key>/<timestamp>", parts[0])
    48  				}
    49  
    50  				key := []byte(keyParts[0])
    51  				timestamp, err := strconv.Atoi(keyParts[1])
    52  				if err != nil {
    53  					return err.Error()
    54  				}
    55  				ikey := pebble.InternalKey{
    56  					UserKey: EncodeKey(MVCCKey{
    57  						Key:       key,
    58  						Timestamp: hlc.Timestamp{WallTime: int64(timestamp)},
    59  					}),
    60  				}
    61  
    62  				value := []byte(parts[1])
    63  				if timestamp == 0 {
    64  					if n, err := fmt.Sscanf(string(value), "timestamp=%d", &timestamp); err != nil {
    65  						return err.Error()
    66  					} else if n != 1 {
    67  						return fmt.Sprintf("malformed txn timestamp: %s, expected timestamp=<value>", value)
    68  					}
    69  					meta := &enginepb.MVCCMetadata{}
    70  					meta.Timestamp.WallTime = int64(timestamp)
    71  					meta.Txn = &enginepb.TxnMeta{}
    72  					var err error
    73  					value, err = protoutil.Marshal(meta)
    74  					if err != nil {
    75  						return err.Error()
    76  					}
    77  				}
    78  
    79  				if err := c.Add(ikey, value); err != nil {
    80  					return err.Error()
    81  				}
    82  			}
    83  
    84  			// Retrieve the properties and sort them for test determinism.
    85  			m := make(map[string]string)
    86  			if err := c.Finish(m); err != nil {
    87  				return err.Error()
    88  			}
    89  			var keys []string
    90  			for k := range m {
    91  				keys = append(keys, k)
    92  			}
    93  			sort.Strings(keys)
    94  
    95  			var buf bytes.Buffer
    96  			for _, k := range keys {
    97  				fmt.Fprintf(&buf, "%s: %x\n", k, m[k])
    98  			}
    99  			return buf.String()
   100  
   101  		default:
   102  			return fmt.Sprintf("unknown command: %s", d.Cmd)
   103  		}
   104  	})
   105  }
   106  
   107  func TestPebbleIterReuse(t *testing.T) {
   108  	defer leaktest.AfterTest(t)()
   109  	// Regression test for https://github.com/cockroachdb/cockroach/issues/42354
   110  	// and similar issues arising from improper re-initialization of cached
   111  	// iterators.
   112  
   113  	eng := createTestPebbleEngine()
   114  	defer eng.Close()
   115  
   116  	batch := eng.NewBatch()
   117  	for i := 0; i < 100; i++ {
   118  		key := MVCCKey{[]byte{byte(i)}, hlc.Timestamp{WallTime: 100}}
   119  		if err := batch.Put(key, []byte("foo")); err != nil {
   120  			t.Fatal(err)
   121  		}
   122  	}
   123  
   124  	iter1 := batch.NewIterator(IterOptions{LowerBound: []byte{40}, UpperBound: []byte{50}})
   125  	valuesCount := 0
   126  	// Seek to a value before the lower bound. Identical to seeking to the lower bound.
   127  	iter1.SeekGE(MVCCKey{Key: []byte{30}})
   128  	for ; ; iter1.Next() {
   129  		ok, err := iter1.Valid()
   130  		if err != nil {
   131  			t.Fatal(err)
   132  		} else if !ok {
   133  			break
   134  		}
   135  		i := iter1.UnsafeKey().Key[0]
   136  		if i < 40 || i >= 50 {
   137  			t.Fatalf("iterator returned key out of bounds: %d", i)
   138  		}
   139  
   140  		valuesCount++
   141  	}
   142  
   143  	if valuesCount != 10 {
   144  		t.Fatalf("expected 10 values, got %d", valuesCount)
   145  	}
   146  	iter1.Close()
   147  
   148  	// Create another iterator, with no lower bound but an upper bound that
   149  	// is lower than the previous iterator's lower bound. This should still result
   150  	// in the right amount of keys being returned; the lower bound from the
   151  	// previous iterator should get zeroed.
   152  	iter2 := batch.NewIterator(IterOptions{UpperBound: []byte{10}})
   153  	valuesCount = 0
   154  	iter1.SeekGE(MVCCKey{Key: []byte{0}})
   155  	for ; ; iter2.Next() {
   156  		ok, err := iter1.Valid()
   157  		if err != nil {
   158  			t.Fatal(err)
   159  		} else if !ok {
   160  			break
   161  		}
   162  
   163  		i := iter2.UnsafeKey().Key[0]
   164  		if i >= 10 {
   165  			t.Fatalf("iterator returned key out of bounds: %d", i)
   166  		}
   167  		valuesCount++
   168  	}
   169  
   170  	if valuesCount != 10 {
   171  		t.Fatalf("expected 10 values, got %d", valuesCount)
   172  	}
   173  	iter2.Close()
   174  }
   175  
   176  func makeMVCCKey(a string) MVCCKey {
   177  	return MVCCKey{Key: []byte(a)}
   178  }
   179  
   180  func TestPebbleSeparatorSuccessor(t *testing.T) {
   181  	defer leaktest.AfterTest(t)()
   182  
   183  	sepCases := []struct {
   184  		a, b, want MVCCKey
   185  	}{
   186  		// Many cases here are adapted from a Pebble unit test.
   187  
   188  		// Non-empty b values.
   189  		{makeMVCCKey("black"), makeMVCCKey("blue"), makeMVCCKey("blb")},
   190  		{makeMVCCKey(""), makeMVCCKey("2"), makeMVCCKey("")},
   191  		{makeMVCCKey("1"), makeMVCCKey("2"), makeMVCCKey("1")},
   192  		{makeMVCCKey("1"), makeMVCCKey("29"), makeMVCCKey("2")},
   193  		{makeMVCCKey("13"), makeMVCCKey("19"), makeMVCCKey("14")},
   194  		{makeMVCCKey("13"), makeMVCCKey("99"), makeMVCCKey("2")},
   195  		{makeMVCCKey("135"), makeMVCCKey("19"), makeMVCCKey("14")},
   196  		{makeMVCCKey("1357"), makeMVCCKey("19"), makeMVCCKey("14")},
   197  		{makeMVCCKey("1357"), makeMVCCKey("2"), makeMVCCKey("14")},
   198  		{makeMVCCKey("13\xff"), makeMVCCKey("14"), makeMVCCKey("13\xff")},
   199  		{makeMVCCKey("13\xff"), makeMVCCKey("19"), makeMVCCKey("14")},
   200  		{makeMVCCKey("1\xff\xff"), makeMVCCKey("19"), makeMVCCKey("1\xff\xff")},
   201  		{makeMVCCKey("1\xff\xff"), makeMVCCKey("2"), makeMVCCKey("1\xff\xff")},
   202  		{makeMVCCKey("1\xff\xff"), makeMVCCKey("9"), makeMVCCKey("2")},
   203  		{makeMVCCKey("1\xfd\xff"), makeMVCCKey("1\xff"), makeMVCCKey("1\xfe")},
   204  		{MVCCKey{
   205  			Key:       []byte("1\xff\xff"),
   206  			Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3},
   207  		}, makeMVCCKey("9"), makeMVCCKey("2")},
   208  		{MVCCKey{
   209  			Key:       []byte("1\xff\xff"),
   210  			Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3},
   211  		}, makeMVCCKey("19"), MVCCKey{
   212  			Key:       []byte("1\xff\xff"),
   213  			Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3},
   214  		},
   215  		},
   216  		// Empty b values.
   217  		{makeMVCCKey(""), makeMVCCKey(""), makeMVCCKey("")},
   218  		{makeMVCCKey("green"), makeMVCCKey(""), makeMVCCKey("green")},
   219  		{makeMVCCKey("1"), makeMVCCKey(""), makeMVCCKey("1")},
   220  		{makeMVCCKey("11\xff"), makeMVCCKey(""), makeMVCCKey("11\xff")},
   221  		{makeMVCCKey("1\xff"), makeMVCCKey(""), makeMVCCKey("1\xff")},
   222  		{makeMVCCKey("1\xff\xff"), makeMVCCKey(""), makeMVCCKey("1\xff\xff")},
   223  		{makeMVCCKey("\xff"), makeMVCCKey(""), makeMVCCKey("\xff")},
   224  		{makeMVCCKey("\xff\xff"), makeMVCCKey(""), makeMVCCKey("\xff\xff")},
   225  	}
   226  	for _, tc := range sepCases {
   227  		t.Run("", func(t *testing.T) {
   228  			got := string(MVCCComparer.Separator(nil, EncodeKey(tc.a), EncodeKey(tc.b)))
   229  			if got != string(EncodeKey(tc.want)) {
   230  				t.Errorf("a, b = %q, %q: got %q, want %q", tc.a, tc.b, got, tc.want)
   231  			}
   232  		})
   233  	}
   234  
   235  	succCases := []struct {
   236  		a, want MVCCKey
   237  	}{
   238  		// Many cases adapted from Pebble test.
   239  		{makeMVCCKey("black"), makeMVCCKey("c")},
   240  		{makeMVCCKey("green"), makeMVCCKey("h")},
   241  		{makeMVCCKey(""), makeMVCCKey("")},
   242  		{makeMVCCKey("13"), makeMVCCKey("2")},
   243  		{makeMVCCKey("135"), makeMVCCKey("2")},
   244  		{makeMVCCKey("13\xff"), makeMVCCKey("2")},
   245  		{MVCCKey{
   246  			Key:       []byte("1\xff\xff"),
   247  			Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3},
   248  		}, makeMVCCKey("2")},
   249  		{makeMVCCKey("\xff"), makeMVCCKey("\xff")},
   250  		{makeMVCCKey("\xff\xff"), makeMVCCKey("\xff\xff")},
   251  		{makeMVCCKey("\xff\xff\xff"), makeMVCCKey("\xff\xff\xff")},
   252  		{makeMVCCKey("\xfe\xff\xff"), makeMVCCKey("\xff")},
   253  		{MVCCKey{
   254  			Key:       []byte("\xff\xff"),
   255  			Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3},
   256  		}, MVCCKey{
   257  			Key:       []byte("\xff\xff"),
   258  			Timestamp: hlc.Timestamp{WallTime: 20, Logical: 3},
   259  		}},
   260  	}
   261  	for _, tc := range succCases {
   262  		t.Run("", func(t *testing.T) {
   263  			got := string(MVCCComparer.Successor(nil, EncodeKey(tc.a)))
   264  			if got != string(EncodeKey(tc.want)) {
   265  				t.Errorf("a = %q: got %q, want %q", tc.a, got, tc.want)
   266  			}
   267  		})
   268  	}
   269  
   270  }
   271  
   272  func BenchmarkMVCCKeyCompare(b *testing.B) {
   273  	rng := rand.New(rand.NewSource(timeutil.Now().Unix()))
   274  	keys := make([][]byte, 1000)
   275  	for i := range keys {
   276  		k := MVCCKey{
   277  			Key: randutil.RandBytes(rng, 8),
   278  			Timestamp: hlc.Timestamp{
   279  				WallTime: int64(rng.Intn(5)),
   280  			},
   281  		}
   282  		keys[i] = EncodeKey(k)
   283  	}
   284  
   285  	b.ResetTimer()
   286  	var c int
   287  	for i, j := 0, 0; i < b.N; i, j = i+1, j+3 {
   288  		c = MVCCKeyCompare(keys[i%len(keys)], keys[j%len(keys)])
   289  	}
   290  	if testing.Verbose() {
   291  		fmt.Fprint(ioutil.Discard, c)
   292  	}
   293  }