github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/sstable/block_test.go (about)

     1  // Copyright 2018 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package sstable
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"strconv"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/petermattis/pebble/internal/base"
    16  	"github.com/petermattis/pebble/internal/datadriven"
    17  	"github.com/stretchr/testify/require"
    18  	"golang.org/x/exp/rand"
    19  )
    20  
    21  func TestBlockWriter(t *testing.T) {
    22  	ikey := func(s string) InternalKey {
    23  		return InternalKey{UserKey: []byte(s)}
    24  	}
    25  
    26  	w := &rawBlockWriter{
    27  		blockWriter: blockWriter{restartInterval: 16},
    28  	}
    29  	w.add(ikey("apple"), nil)
    30  	w.add(ikey("apricot"), nil)
    31  	w.add(ikey("banana"), nil)
    32  	block := w.finish()
    33  
    34  	expected := []byte(
    35  		"\x00\x05\x00apple" +
    36  			"\x02\x05\x00ricot" +
    37  			"\x00\x06\x00banana" +
    38  			"\x00\x00\x00\x00\x01\x00\x00\x00")
    39  	if !bytes.Equal(expected, block) {
    40  		t.Fatalf("expected\n%q\nfound\n%q", expected, block)
    41  	}
    42  }
    43  
    44  func TestBlockIter(t *testing.T) {
    45  	// k is a block that maps three keys "apple", "apricot", "banana" to empty strings.
    46  	k := block([]byte(
    47  		"\x00\x05\x00apple" +
    48  			"\x02\x05\x00ricot" +
    49  			"\x00\x06\x00banana" +
    50  			"\x00\x00\x00\x00\x01\x00\x00\x00"))
    51  	var testcases = []struct {
    52  		index int
    53  		key   string
    54  	}{
    55  		{0, ""},
    56  		{0, "a"},
    57  		{0, "aaaaaaaaaaaaaaa"},
    58  		{0, "app"},
    59  		{0, "apple"},
    60  		{1, "appliance"},
    61  		{1, "apricos"},
    62  		{1, "apricot"},
    63  		{2, "azzzzzzzzzzzzzz"},
    64  		{2, "b"},
    65  		{2, "banan"},
    66  		{2, "banana"},
    67  		{3, "banana\x00"},
    68  		{3, "c"},
    69  	}
    70  	for _, tc := range testcases {
    71  		i, err := newRawBlockIter(bytes.Compare, k)
    72  		if err != nil {
    73  			t.Fatal(err)
    74  		}
    75  		i.SeekGE([]byte(tc.key))
    76  		for j, kWant := range []string{"apple", "apricot", "banana"}[tc.index:] {
    77  			if !i.Valid() {
    78  				t.Fatalf("key=%q, index=%d, j=%d: Valid got false, want true", tc.key, tc.index, j)
    79  			}
    80  			if kGot := string(i.Key().UserKey); kGot != kWant {
    81  				t.Fatalf("key=%q, index=%d, j=%d: got %q, want %q", tc.key, tc.index, j, kGot, kWant)
    82  			}
    83  			i.Next()
    84  		}
    85  		if i.Valid() {
    86  			t.Fatalf("key=%q, index=%d: Valid got true, want false", tc.key, tc.index)
    87  		}
    88  		if err := i.Close(); err != nil {
    89  			t.Fatalf("key=%q, index=%d: got err=%v", tc.key, tc.index, err)
    90  		}
    91  	}
    92  
    93  	{
    94  		i, err := newRawBlockIter(bytes.Compare, k)
    95  		if err != nil {
    96  			t.Fatal(err)
    97  		}
    98  		i.Last()
    99  		for j, kWant := range []string{"banana", "apricot", "apple"} {
   100  			if !i.Valid() {
   101  				t.Fatalf("j=%d: Valid got false, want true", j)
   102  			}
   103  			if kGot := string(i.Key().UserKey); kGot != kWant {
   104  				t.Fatalf("j=%d: got %q, want %q", j, kGot, kWant)
   105  			}
   106  			i.Prev()
   107  		}
   108  		if i.Valid() {
   109  			t.Fatalf("Valid got true, want false")
   110  		}
   111  		if err := i.Close(); err != nil {
   112  			t.Fatalf("got err=%v", err)
   113  		}
   114  	}
   115  }
   116  
   117  func TestBlockIter2(t *testing.T) {
   118  	makeIkey := func(s string) InternalKey {
   119  		j := strings.Index(s, ":")
   120  		seqNum, err := strconv.Atoi(s[j+1:])
   121  		if err != nil {
   122  			panic(err)
   123  		}
   124  		return base.MakeInternalKey([]byte(s[:j]), uint64(seqNum), InternalKeyKindSet)
   125  	}
   126  
   127  	var block []byte
   128  
   129  	for _, r := range []int{1, 2, 3, 4} {
   130  		t.Run(fmt.Sprintf("restart=%d", r), func(t *testing.T) {
   131  			datadriven.RunTest(t, "testdata/block", func(d *datadriven.TestData) string {
   132  				switch d.Cmd {
   133  				case "build":
   134  					w := &blockWriter{restartInterval: r}
   135  					for _, e := range strings.Split(strings.TrimSpace(d.Input), ",") {
   136  						w.add(makeIkey(e), nil)
   137  					}
   138  					block = w.finish()
   139  					return ""
   140  
   141  				case "iter":
   142  					iter, err := newBlockIter(bytes.Compare, block)
   143  					if err != nil {
   144  						return err.Error()
   145  					}
   146  
   147  					for _, arg := range d.CmdArgs {
   148  						switch arg.Key {
   149  						case "globalSeqNum":
   150  							if len(arg.Vals) != 1 {
   151  								return fmt.Sprintf("%s: arg %s expects 1 value", d.Cmd, arg.Key)
   152  							}
   153  							v, err := strconv.Atoi(arg.Vals[0])
   154  							if err != nil {
   155  								return err.Error()
   156  							}
   157  							iter.globalSeqNum = uint64(v)
   158  						default:
   159  							return fmt.Sprintf("%s: unknown arg: %s", d.Cmd, arg.Key)
   160  						}
   161  					}
   162  
   163  					var b bytes.Buffer
   164  					for _, line := range strings.Split(d.Input, "\n") {
   165  						parts := strings.Fields(line)
   166  						if len(parts) == 0 {
   167  							continue
   168  						}
   169  						switch parts[0] {
   170  						case "seek-ge":
   171  							if len(parts) != 2 {
   172  								return fmt.Sprintf("seek-ge <key>\n")
   173  							}
   174  							iter.SeekGE([]byte(strings.TrimSpace(parts[1])))
   175  						case "seek-lt":
   176  							if len(parts) != 2 {
   177  								return fmt.Sprintf("seek-lt <key>\n")
   178  							}
   179  							iter.SeekLT([]byte(strings.TrimSpace(parts[1])))
   180  						case "first":
   181  							iter.First()
   182  						case "last":
   183  							iter.Last()
   184  						case "next":
   185  							iter.Next()
   186  						case "prev":
   187  							iter.Prev()
   188  						}
   189  						if iter.Valid() {
   190  							fmt.Fprintf(&b, "<%s:%d>", iter.Key().UserKey, iter.Key().SeqNum())
   191  						} else if err := iter.Error(); err != nil {
   192  							fmt.Fprintf(&b, "<err=%v>", err)
   193  						} else {
   194  							fmt.Fprintf(&b, ".")
   195  						}
   196  					}
   197  					b.WriteString("\n")
   198  					return b.String()
   199  
   200  				default:
   201  					return fmt.Sprintf("unknown command: %s", d.Cmd)
   202  				}
   203  			})
   204  		})
   205  	}
   206  }
   207  
   208  func TestBlockIterKeyStability(t *testing.T) {
   209  	w := &blockWriter{restartInterval: 1}
   210  	expected := [][]byte{
   211  		[]byte("apple"),
   212  		[]byte("apricot"),
   213  		[]byte("banana"),
   214  	}
   215  	for i := range expected {
   216  		w.add(InternalKey{UserKey: expected[i]}, nil)
   217  	}
   218  	block := w.finish()
   219  
   220  	i, err := newBlockIter(bytes.Compare, block)
   221  	if err != nil {
   222  		t.Fatal(err)
   223  	}
   224  	// Loop over the block entries, storing each key slice.
   225  	var keys [][]byte
   226  	for key, _ := i.First(); key != nil; key, _ = i.Next() {
   227  		keys = append(keys, key.UserKey)
   228  	}
   229  
   230  	// Check that the slices match our expected values. Note that this is only
   231  	// guaranteed because of the usage of a restart-interval of 1 so that prefix
   232  	// compression was not performed.
   233  	require.EqualValues(t, expected, keys)
   234  }
   235  
   236  func BenchmarkBlockIterSeekGE(b *testing.B) {
   237  	const blockSize = 32 << 10
   238  
   239  	for _, restartInterval := range []int{16} {
   240  		b.Run(fmt.Sprintf("restart=%d", restartInterval),
   241  			func(b *testing.B) {
   242  				w := &blockWriter{
   243  					restartInterval: restartInterval,
   244  				}
   245  
   246  				var ikey InternalKey
   247  				var keys [][]byte
   248  				for i := 0; w.estimatedSize() < blockSize; i++ {
   249  					key := []byte(fmt.Sprintf("%05d", i))
   250  					keys = append(keys, key)
   251  					ikey.UserKey = key
   252  					w.add(ikey, nil)
   253  				}
   254  
   255  				it, err := newBlockIter(bytes.Compare, w.finish())
   256  				if err != nil {
   257  					b.Fatal(err)
   258  				}
   259  				rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   260  
   261  				b.ResetTimer()
   262  				for i := 0; i < b.N; i++ {
   263  					k := keys[rng.Intn(len(keys))]
   264  					it.SeekGE(k)
   265  					if testing.Verbose() {
   266  						if !it.Valid() {
   267  							b.Fatal("expected to find key")
   268  						}
   269  						if !bytes.Equal(k, it.Key().UserKey) {
   270  							b.Fatalf("expected %s, but found %s", k, it.Key().UserKey)
   271  						}
   272  					}
   273  				}
   274  			})
   275  	}
   276  }
   277  
   278  func BenchmarkBlockIterSeekLT(b *testing.B) {
   279  	const blockSize = 32 << 10
   280  
   281  	for _, restartInterval := range []int{16} {
   282  		b.Run(fmt.Sprintf("restart=%d", restartInterval),
   283  			func(b *testing.B) {
   284  				w := &blockWriter{
   285  					restartInterval: restartInterval,
   286  				}
   287  
   288  				var ikey InternalKey
   289  				var keys [][]byte
   290  				for i := 0; w.estimatedSize() < blockSize; i++ {
   291  					key := []byte(fmt.Sprintf("%05d", i))
   292  					keys = append(keys, key)
   293  					ikey.UserKey = key
   294  					w.add(ikey, nil)
   295  				}
   296  
   297  				it, err := newBlockIter(bytes.Compare, w.finish())
   298  				if err != nil {
   299  					b.Fatal(err)
   300  				}
   301  				rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   302  
   303  				b.ResetTimer()
   304  				for i := 0; i < b.N; i++ {
   305  					j := rng.Intn(len(keys))
   306  					it.SeekLT(keys[j])
   307  					if testing.Verbose() {
   308  						if j == 0 {
   309  							if it.Valid() {
   310  								b.Fatal("unexpected key")
   311  							}
   312  						} else {
   313  							if !it.Valid() {
   314  								b.Fatal("expected to find key")
   315  							}
   316  							k := keys[j-1]
   317  							if !bytes.Equal(k, it.Key().UserKey) {
   318  								b.Fatalf("expected %s, but found %s", k, it.Key().UserKey)
   319  							}
   320  						}
   321  					}
   322  				}
   323  			})
   324  	}
   325  }
   326  
   327  func BenchmarkBlockIterNext(b *testing.B) {
   328  	const blockSize = 32 << 10
   329  
   330  	for _, restartInterval := range []int{16} {
   331  		b.Run(fmt.Sprintf("restart=%d", restartInterval),
   332  			func(b *testing.B) {
   333  				w := &blockWriter{
   334  					restartInterval: restartInterval,
   335  				}
   336  
   337  				var ikey InternalKey
   338  				for i := 0; w.estimatedSize() < blockSize; i++ {
   339  					ikey.UserKey = []byte(fmt.Sprintf("%05d", i))
   340  					w.add(ikey, nil)
   341  				}
   342  
   343  				it, err := newBlockIter(bytes.Compare, w.finish())
   344  				if err != nil {
   345  					b.Fatal(err)
   346  				}
   347  
   348  				b.ResetTimer()
   349  				for i := 0; i < b.N; i++ {
   350  					if !it.Valid() {
   351  						it.First()
   352  					}
   353  					it.Next()
   354  				}
   355  			})
   356  	}
   357  }
   358  
   359  func BenchmarkBlockIterPrev(b *testing.B) {
   360  	const blockSize = 32 << 10
   361  
   362  	for _, restartInterval := range []int{16} {
   363  		b.Run(fmt.Sprintf("restart=%d", restartInterval),
   364  			func(b *testing.B) {
   365  				w := &blockWriter{
   366  					restartInterval: restartInterval,
   367  				}
   368  
   369  				var ikey InternalKey
   370  				for i := 0; w.estimatedSize() < blockSize; i++ {
   371  					ikey.UserKey = []byte(fmt.Sprintf("%05d", i))
   372  					w.add(ikey, nil)
   373  				}
   374  
   375  				it, err := newBlockIter(bytes.Compare, w.finish())
   376  				if err != nil {
   377  					b.Fatal(err)
   378  				}
   379  
   380  				b.ResetTimer()
   381  				for i := 0; i < b.N; i++ {
   382  					if !it.Valid() {
   383  						it.Last()
   384  					}
   385  					it.Prev()
   386  				}
   387  			})
   388  	}
   389  }