github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/libs/db/internal/dbtest/dbtest.go (about) 1 package dbtest 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "math/rand" 7 "os" 8 "path/filepath" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 lldb "github.com/lazyledger/lazyledger-core/libs/db" 15 ) 16 17 //---------------------------------------- 18 // Helper functions. 19 20 func Valid(t *testing.T, itr lldb.Iterator, expected bool) { 21 valid := itr.Valid() 22 require.Equal(t, expected, valid) 23 } 24 25 func Next(t *testing.T, itr lldb.Iterator, expected bool) { 26 itr.Next() 27 // assert.NoError(t, err) TODO: look at fixing this 28 valid := itr.Valid() 29 require.Equal(t, expected, valid) 30 } 31 32 func NextPanics(t *testing.T, itr lldb.Iterator) { 33 assert.Panics(t, func() { itr.Next() }, "checkNextPanics expected an error but didn't") 34 } 35 36 func Domain(t *testing.T, itr lldb.Iterator, start, end []byte) { 37 ds, de := itr.Domain() 38 assert.Equal(t, start, ds, "checkDomain domain start incorrect") 39 assert.Equal(t, end, de, "checkDomain domain end incorrect") 40 } 41 42 func Item(t *testing.T, itr lldb.Iterator, key []byte, value []byte) { 43 v := itr.Value() 44 45 k := itr.Key() 46 47 assert.Exactly(t, key, k) 48 assert.Exactly(t, value, v) 49 } 50 51 func Invalid(t *testing.T, itr lldb.Iterator) { 52 Valid(t, itr, false) 53 KeyPanics(t, itr) 54 ValuePanics(t, itr) 55 NextPanics(t, itr) 56 } 57 58 func KeyPanics(t *testing.T, itr lldb.Iterator) { 59 assert.Panics(t, func() { itr.Key() }, "checkKeyPanics expected panic but didn't") 60 } 61 62 func Value(t *testing.T, db lldb.DB, key []byte, valueWanted []byte) { 63 valueGot, err := db.Get(key) 64 assert.NoError(t, err) 65 assert.Equal(t, valueWanted, valueGot) 66 } 67 68 func ValuePanics(t *testing.T, itr lldb.Iterator) { 69 assert.Panics(t, func() { itr.Value() }) 70 } 71 72 func CleanupDBDir(dir, name string) { 73 err := os.RemoveAll(filepath.Join(dir, name) + ".db") 74 if err != nil { 75 panic(err) 76 } 77 } 78 79 const strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters 80 81 // RandStr constructs a random alphanumeric string of given length. 82 func RandStr(length int) string { 83 chars := []byte{} 84 MAIN_LOOP: 85 for { 86 val := rand.Int63() // nolint:gosec // G404: Use of weak random number generator 87 for i := 0; i < 10; i++ { 88 v := int(val & 0x3f) // rightmost 6 bits 89 if v >= 62 { // only 62 characters in strChars 90 val >>= 6 91 continue 92 } else { 93 chars = append(chars, strChars[v]) 94 if len(chars) == length { 95 break MAIN_LOOP 96 } 97 val >>= 6 98 } 99 } 100 } 101 102 return string(chars) 103 } 104 105 func Int642Bytes(i int64) []byte { 106 buf := make([]byte, 8) 107 binary.BigEndian.PutUint64(buf, uint64(i)) 108 return buf 109 } 110 111 func Bytes2Int64(buf []byte) int64 { 112 return int64(binary.BigEndian.Uint64(buf)) 113 } 114 115 func BenchmarkRangeScans(b *testing.B, db lldb.DB, dbSize int64) { 116 b.StopTimer() 117 118 rangeSize := int64(10000) 119 if dbSize < rangeSize { 120 b.Errorf("db size %v cannot be less than range size %v", dbSize, rangeSize) 121 } 122 123 for i := int64(0); i < dbSize; i++ { 124 bytes := Int642Bytes(i) 125 err := db.Set(bytes, bytes) 126 if err != nil { 127 // require.NoError() is very expensive (according to profiler), so check manually 128 b.Fatal(b, err) 129 } 130 } 131 b.StartTimer() 132 133 for i := 0; i < b.N; i++ { 134 135 start := rand.Int63n(dbSize - rangeSize) // nolint: gosec 136 end := start + rangeSize 137 iter, err := db.Iterator(Int642Bytes(start), Int642Bytes(end)) 138 require.NoError(b, err) 139 count := 0 140 for ; iter.Valid(); iter.Next() { 141 count++ 142 } 143 iter.Close() 144 require.EqualValues(b, rangeSize, count) 145 } 146 } 147 148 func BenchmarkRandomReadsWrites(b *testing.B, db lldb.DB) { 149 b.StopTimer() 150 151 // create dummy data 152 const numItems = int64(1000000) 153 internal := map[int64]int64{} 154 for i := 0; i < int(numItems); i++ { 155 internal[int64(i)] = int64(0) 156 } 157 158 // fmt.Println("ok, starting") 159 b.StartTimer() 160 161 for i := 0; i < b.N; i++ { 162 // Write something 163 { 164 idx := rand.Int63n(numItems) // nolint: gosec 165 internal[idx]++ 166 val := internal[idx] 167 idxBytes := Int642Bytes(idx) 168 valBytes := Int642Bytes(val) 169 // fmt.Printf("Set %X -> %X\n", idxBytes, valBytes) 170 err := db.Set(idxBytes, valBytes) 171 if err != nil { 172 // require.NoError() is very expensive (according to profiler), so check manually 173 b.Fatal(b, err) 174 } 175 } 176 177 // Read something 178 { 179 idx := rand.Int63n(numItems) // nolint: gosec 180 valExp := internal[idx] 181 idxBytes := Int642Bytes(idx) 182 valBytes, err := db.Get(idxBytes) 183 if err != nil { 184 // require.NoError() is very expensive (according to profiler), so check manually 185 b.Fatal(b, err) 186 } 187 // fmt.Printf("Get %X -> %X\n", idxBytes, valBytes) 188 if valExp == 0 { 189 if !bytes.Equal(valBytes, nil) { 190 b.Errorf("Expected %v for %v, got %X", nil, idx, valBytes) 191 break 192 } 193 } else { 194 if len(valBytes) != 8 { 195 b.Errorf("Expected length 8 for %v, got %X", idx, valBytes) 196 break 197 } 198 valGot := Bytes2Int64(valBytes) 199 if valExp != valGot { 200 b.Errorf("Expected %v for %v, got %v", valExp, idx, valGot) 201 break 202 } 203 } 204 } 205 206 } 207 }