github.com/bcskill/bcschain/v3@v3.4.9-beta2/ethdb/file_segment_test.go (about) 1 package ethdb_test 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "math" 7 "math/rand" 8 "os" 9 "reflect" 10 "testing" 11 "testing/quick" 12 13 "github.com/bcskill/bcschain/v3/common" 14 15 "github.com/bcskill/bcschain/v3/ethdb" 16 ) 17 18 func TestFileSegment_Get(t *testing.T) { 19 t.Run("OK", func(t *testing.T) { 20 path := MustTempFile() 21 defer os.Remove(path) 22 23 // Encode key/values to file segment. 24 enc := ethdb.NewFileSegmentEncoder(path) 25 if err := enc.Open(); err != nil { 26 t.Fatal(err) 27 } else if err := enc.EncodeKeyValue([]byte("foo"), []byte("bar")); err != nil { 28 t.Fatal(err) 29 } else if err := enc.Flush(); err != nil { 30 t.Fatal(err) 31 } else if err := enc.Close(); err != nil { 32 t.Fatal(err) 33 } 34 35 // Open as file segment. 36 s := ethdb.NewFileSegment("test", path) 37 if err := s.Open(); err != nil { 38 t.Fatal(err) 39 } 40 defer s.Close() 41 42 // Fetch existing keys. 43 if v, err := s.Get([]byte("foo")); err != nil { 44 t.Fatal(err) 45 } else if string(v) != "bar" { 46 t.Fatalf("unexpected value: %q", v) 47 } 48 49 // Fetch unknown key. 50 if v, err := s.Get([]byte("no_such_key")); err != common.ErrNotFound { 51 t.Fatalf("unexpected error: %s", err) 52 } else if v != nil { 53 t.Fatalf("expected nil value, got %q", v) 54 } 55 56 // Close file segment. 57 if err := s.Close(); err != nil { 58 t.Fatal(err) 59 } 60 }) 61 } 62 63 // Ensure ethdb.FileSegment can fetch keys using randomized test data. 64 func TestFileSegment_Quick(t *testing.T) { 65 if testing.Short() { 66 t.Skip("short") 67 } 68 69 const maxCount = 10000 70 const maxKeyLen = 2000 71 const maxValueLen = 10000 72 73 quick.Check(func(keys, values [][]byte) bool { 74 path := MustTempFile() 75 defer os.Remove(path) 76 77 // Write data to file. 78 if err := EncodeToFileSegment(path, keys, values); err != nil { 79 t.Fatal(err) 80 } 81 82 // Open as file. 83 s := ethdb.NewFileSegment("test", path) 84 if err := s.Open(); err != nil { 85 t.Fatal(err) 86 } 87 defer s.Close() 88 89 // Verify all key/value pairs exist. 90 for i := range keys { 91 if v, err := s.Get(keys[i]); err != nil { 92 t.Fatal(err) 93 } else if !bytes.Equal(values[i], v) { 94 t.Fatalf("value mismatch: (key=%q) expected %q, got %q", keys[i], values[i], v) 95 } 96 } 97 98 // Verify we can iterate them in order. 99 itr, i := s.Iterator(), 0 100 for ; itr.Next(); i++ { 101 if !bytes.Equal(itr.Key(), keys[i]) { 102 t.Fatalf("iterator key mismatch:\nexpected %x\ngot %x", keys[i], itr.Key()) 103 } else if !bytes.Equal(itr.Value(), values[i]) { 104 t.Fatal("iterator value mismatch") 105 } 106 } 107 if i != len(keys) { 108 t.Fatal("short iterator") 109 } 110 111 return true 112 }, &quick.Config{ 113 MaxCount: 10, 114 Values: func(args []reflect.Value, rand *rand.Rand) { 115 n := rand.Intn(maxCount-1) + 1 116 args[0] = reflect.ValueOf(generateKeys(n, 1, maxKeyLen-1, rand)) 117 args[1] = reflect.ValueOf(generateValues(n, 0, maxValueLen, rand)) 118 }, 119 }) 120 } 121 122 func BenchmarkFileSegment_Get(b *testing.B) { 123 path := MustTempFile() 124 defer os.Remove(path) 125 126 // Encode key/value pairs. 127 const n = 100000 128 keys, values := make([][]byte, n), make([][]byte, n) 129 for i := 0; i < n; i++ { 130 keys[i] = make([]byte, 32) 131 binary.BigEndian.PutUint64(keys[i], uint64(i)) 132 values[i] = make([]byte, 1024) 133 } 134 EncodeToFileSegment(path, keys, values) 135 136 // Determine random access pattern. 137 perm := rand.Perm(n) 138 139 // Open as file segment. 140 s := ethdb.NewFileSegment("test", path) 141 if err := s.Open(); err != nil { 142 b.Fatal(err) 143 } 144 defer s.Close() 145 146 b.ResetTimer() 147 b.ReportAllocs() 148 149 // Lookup key/value pairs. 150 for i := 0; i < b.N; i++ { 151 key := keys[perm[i%len(perm)]] 152 if v, err := s.Get(key); err != nil { 153 b.Fatal(err) 154 } else if v == nil { 155 b.Fatal("key not found") 156 } 157 } 158 } 159 160 // EncodeToFileSegment encodes a set of key/value pairs to an ethdb.FileSegment at path. 161 func EncodeToFileSegment(path string, keys, values [][]byte) error { 162 // Build file segment. 163 enc := ethdb.NewFileSegmentEncoder(path) 164 if err := enc.Open(); err != nil { 165 return err 166 } 167 defer enc.Close() 168 169 // Write all keys. 170 for i := range keys { 171 if err := enc.EncodeKeyValue(keys[i], values[i]); err != nil { 172 return err 173 } 174 } 175 176 // Flush all data. 177 if err := enc.Flush(); err != nil { 178 return err 179 } else if err := enc.Close(); err != nil { 180 return err 181 } 182 return nil 183 } 184 185 // generateKeys returns a set of n unique, randomly generated keys. 186 func generateKeys(n, min, max int, rand *rand.Rand) [][]byte { 187 a := make([][]byte, n) 188 for i, m := 0, make(map[string]struct{}); i < len(a); i++ { 189 a[i] = generateBytes(min, max, rand) 190 if _, ok := m[string(a[i])]; ok { 191 i-- 192 continue 193 } 194 m[string(a[i])] = struct{}{} 195 } 196 return a 197 } 198 199 // generateValues returns a set of n randomly generated values. 200 func generateValues(n, min, max int, rand *rand.Rand) [][]byte { 201 a := make([][]byte, n) 202 for i := range a { 203 a[i] = generateBytes(min, max, rand) 204 } 205 return a 206 } 207 208 // generateBytes returns a randomly generated byte slice between min & max length. 209 func generateBytes(min, max int, rand *rand.Rand) []byte { 210 b := make([]byte, rand.Intn(max-min)+min) 211 for i := range b { 212 b[i] = byte(rand.Intn(math.MaxInt8)) 213 } 214 return b 215 }