github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/ptable/table_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 ptable
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/petermattis/pebble/cache"
    14  	"github.com/petermattis/pebble/internal/base"
    15  	"github.com/petermattis/pebble/vfs"
    16  	"golang.org/x/exp/rand"
    17  )
    18  
    19  type testEnv []ColumnDef
    20  
    21  func (e testEnv) Encode(row RowReader, buf []byte) (key, value []byte) {
    22  	for i := range e {
    23  		if row.Null(i) {
    24  			continue
    25  		}
    26  		switch e[i].Type {
    27  		case ColumnTypeInt64:
    28  			key = append(key, []byte(fmt.Sprintf("%08d", row.Int64(i)))...)
    29  		default:
    30  			panic("not reached")
    31  		}
    32  	}
    33  
    34  	return key, nil
    35  }
    36  
    37  func (e testEnv) Decode(key, value, buf []byte, writer RowWriter) {
    38  }
    39  
    40  func newEnv(schema ...ColumnDef) *Env {
    41  	t := testEnv(schema)
    42  	return &Env{
    43  		Schema: schema,
    44  		Encode: t.Encode,
    45  		Decode: t.Decode,
    46  	}
    47  }
    48  
    49  type testRow []interface{}
    50  
    51  func makeRow(cols ...interface{}) testRow {
    52  	return testRow(cols)
    53  }
    54  
    55  func (r testRow) Null(col int) bool {
    56  	return r[col] == nil
    57  }
    58  
    59  func (r testRow) Bool(col int) bool {
    60  	return r[col].(bool)
    61  }
    62  
    63  func (r testRow) Int8(col int) int8 {
    64  	return r[col].(int8)
    65  }
    66  
    67  func (r testRow) Int16(col int) int16 {
    68  	return r[col].(int16)
    69  }
    70  
    71  func (r testRow) Int32(col int) int32 {
    72  	return r[col].(int32)
    73  }
    74  
    75  func (r testRow) Int64(col int) int64 {
    76  	return r[col].(int64)
    77  }
    78  
    79  func (r testRow) Float32(col int) float32 {
    80  	return r[col].(float32)
    81  }
    82  
    83  func (r testRow) Float64(col int) float64 {
    84  	return r[col].(float64)
    85  }
    86  
    87  func (r testRow) Bytes(col int) []byte {
    88  	switch t := r[col].(type) {
    89  	case []byte:
    90  		return t
    91  	case string:
    92  		return []byte(t)
    93  	default:
    94  		panic("not reached")
    95  	}
    96  }
    97  
    98  func TestTable(t *testing.T) {
    99  	const count int64 = 1000
   100  	mem := vfs.NewMem()
   101  	env := newEnv(ColumnDef{Type: ColumnTypeInt64})
   102  
   103  	{
   104  		f, err := mem.Create("test")
   105  		if err != nil {
   106  			t.Fatal(err)
   107  		}
   108  		w := NewWriter(f, env, nil, &base.LevelOptions{BlockSize: 100})
   109  		for i := int64(0); i < count; i++ {
   110  			if err := w.AddRow(makeRow(i)); err != nil {
   111  				t.Fatal(err)
   112  			}
   113  		}
   114  		if err := w.Close(); err != nil {
   115  			t.Fatal(err)
   116  		}
   117  	}
   118  
   119  	{
   120  		f, err := mem.Open("test")
   121  		if err != nil {
   122  			t.Fatal(err)
   123  		}
   124  		r := NewReader(f, 0, nil)
   125  		iter := r.NewIter()
   126  		var j int64
   127  		for iter.First(); iter.Valid(); iter.Next() {
   128  			col := iter.Block().Column(0).Int64()
   129  			for _, i := range col {
   130  				if j != i {
   131  					t.Fatalf("expected %d, but found %d", j, i)
   132  				}
   133  				j++
   134  			}
   135  		}
   136  		if count != j {
   137  			t.Fatalf("expected %d, but found %d", count, j)
   138  		}
   139  
   140  		for i := int64(0); i < count; i++ {
   141  			key, _ := env.Encode(makeRow(i), nil)
   142  			iter.SeekGE(key)
   143  			if !iter.Valid() {
   144  				t.Fatal("expected valid iterator")
   145  			}
   146  			var found bool
   147  			for _, v := range iter.Block().Column(0).Int64() {
   148  				if i == v {
   149  					found = true
   150  					break
   151  				}
   152  			}
   153  			if !found {
   154  				t.Fatalf("unable to find %d on block %d", i, iter.pos)
   155  			}
   156  		}
   157  
   158  		if err := r.Close(); err != nil {
   159  			t.Fatal(err)
   160  		}
   161  	}
   162  }
   163  
   164  func buildBenchmarkTable(b *testing.B, blockSize int, nullValues bool) (*Reader, [][]byte) {
   165  	mem := vfs.NewMem()
   166  	f0, err := mem.Create("bench")
   167  	if err != nil {
   168  		b.Fatal(err)
   169  	}
   170  	defer f0.Close()
   171  
   172  	env := newEnv(ColumnDef{Type: ColumnTypeInt64}, ColumnDef{Type: ColumnTypeInt64})
   173  	w := NewWriter(f0, env, nil, &base.LevelOptions{BlockSize: blockSize})
   174  	var keys [][]byte
   175  	for i := int64(0); i < 1e6; i++ {
   176  		var r testRow
   177  		if nullValues && (i%2) == 0 {
   178  			r = makeRow(i, nil)
   179  		} else {
   180  			r = makeRow(i, i)
   181  		}
   182  		w.AddRow(r)
   183  		key, _ := env.Encode(r, nil)
   184  		keys = append(keys, key)
   185  	}
   186  	if err := w.Close(); err != nil {
   187  		b.Fatal(err)
   188  	}
   189  
   190  	// Re-open that filename for reading.
   191  	f1, err := mem.Open("bench")
   192  	if err != nil {
   193  		b.Fatal(err)
   194  	}
   195  	return NewReader(f1, 0, &base.Options{
   196  		Cache: cache.New(128 << 20),
   197  	}), keys
   198  }
   199  
   200  func BenchmarkTableIterSeekGE(b *testing.B) {
   201  	const blockSize = 32 << 10
   202  
   203  	r, keys := buildBenchmarkTable(b, blockSize, false /* NULL values */)
   204  	it := r.NewIter()
   205  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   206  
   207  	b.ResetTimer()
   208  	for i := 0; i < b.N; i++ {
   209  		v := int64(rng.Intn(len(keys)))
   210  		it.SeekGE(keys[v])
   211  		if !it.Valid() {
   212  			b.Fatalf("unable to find block containing %d", v)
   213  		}
   214  		vals := it.Block().Column(0).Int64()
   215  		index := sort.Search(len(vals), func(j int) bool {
   216  			return vals[j] >= v
   217  		})
   218  		if vals[index] != v {
   219  			b.Fatalf("unable to find %d", v)
   220  		}
   221  	}
   222  }
   223  
   224  func BenchmarkTableIterNext(b *testing.B) {
   225  	const blockSize = 32 << 10
   226  
   227  	r, _ := buildBenchmarkTable(b, blockSize, true /* NULL values */)
   228  	it := r.NewIter()
   229  
   230  	b.ResetTimer()
   231  	var sum int64
   232  	for i, k := 0, 0; i < b.N; i += k {
   233  		if !it.Valid() {
   234  			it.First()
   235  		}
   236  
   237  		col := it.Block().Column(1)
   238  		vals := col.Int64()
   239  		k = int(col.N)
   240  		if k > b.N-i {
   241  			k = b.N - i
   242  		}
   243  		for j := 0; j < k; j++ {
   244  			if r := col.Rank(j); r >= 0 {
   245  				sum += vals[r]
   246  			}
   247  		}
   248  
   249  		it.Next()
   250  	}
   251  	if testing.Verbose() {
   252  		fmt.Println(sum)
   253  	}
   254  }
   255  
   256  func BenchmarkTableIterPrev(b *testing.B) {
   257  	const blockSize = 32 << 10
   258  
   259  	r, _ := buildBenchmarkTable(b, blockSize, true /* NULL values */)
   260  	it := r.NewIter()
   261  
   262  	b.ResetTimer()
   263  	var sum int64
   264  	for i, k := 0, 0; i < b.N; i += k {
   265  		if !it.Valid() {
   266  			it.Last()
   267  		}
   268  
   269  		col := it.Block().Column(1)
   270  		vals := col.Int64()
   271  		k = int(col.N)
   272  		if k > b.N-i {
   273  			k = b.N - i
   274  		}
   275  		for j, e := int(col.N)-1, int(col.N)-k; j >= e; j-- {
   276  			if r := col.Rank(j); r >= 0 {
   277  				sum += vals[r]
   278  			}
   279  		}
   280  
   281  		it.Prev()
   282  	}
   283  	if testing.Verbose() {
   284  		fmt.Println(sum)
   285  	}
   286  }