github.com/klaytn/klaytn@v1.10.2/storage/database/database_test.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2014 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from ethdb/database_test.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package database 22 23 import ( 24 "bytes" 25 "fmt" 26 "io/ioutil" 27 "math/big" 28 "os" 29 "strconv" 30 "strings" 31 "sync" 32 "testing" 33 34 "github.com/klaytn/klaytn/common" 35 "github.com/stretchr/testify/assert" 36 "github.com/stretchr/testify/suite" 37 ) 38 39 func newTestLDB() (Database, func()) { 40 dirName, err := ioutil.TempDir(os.TempDir(), "klay_leveldb_test_") 41 if err != nil { 42 panic("failed to create test file: " + err.Error()) 43 } 44 db, err := NewLevelDBWithOption(dirName, GetDefaultLevelDBOption()) 45 if err != nil { 46 panic("failed to create test database: " + err.Error()) 47 } 48 49 return db, func() { 50 db.Close() 51 os.RemoveAll(dirName) 52 } 53 } 54 55 func newTestBadgerDB() (Database, func()) { 56 dirName, err := ioutil.TempDir(os.TempDir(), "klay_badgerdb_test_") 57 if err != nil { 58 panic("failed to create test file: " + err.Error()) 59 } 60 db, err := NewBadgerDB(dirName) 61 if err != nil { 62 panic("failed to create test database: " + err.Error()) 63 } 64 65 return db, func() { 66 db.Close() 67 os.RemoveAll(dirName) 68 } 69 } 70 71 func newTestMemDB() (Database, func()) { 72 return NewMemDB(), func() {} 73 } 74 75 func newTestDynamoS3DB() (Database, func()) { 76 // to start test with DynamoDB singletons 77 oldDynamoDBClient := dynamoDBClient 78 dynamoDBClient = nil 79 80 oldDynamoOnceWorker := dynamoOnceWorker 81 dynamoOnceWorker = &sync.Once{} 82 83 oldDynamoWriteCh := dynamoWriteCh 84 dynamoWriteCh = nil 85 86 db, err := newDynamoDB(GetTestDynamoConfig()) 87 if err != nil { 88 panic("failed to create test DynamoS3 database: " + err.Error()) 89 } 90 return db, func() { 91 db.Close() 92 db.deleteTable() 93 db.fdb.deleteBucket() 94 95 // to finish test with DynamoDB singletons 96 dynamoDBClient = oldDynamoDBClient 97 dynamoOnceWorker = oldDynamoOnceWorker 98 dynamoWriteCh = oldDynamoWriteCh 99 } 100 } 101 102 type commonDatabaseTestSuite struct { 103 suite.Suite 104 database Database 105 } 106 107 func TestDatabaseTestSuite(t *testing.T) { 108 // If you want to include dynamo test, use below line 109 // var testDatabases = []func() (Database, func()){newTestLDB, newTestBadgerDB, newTestMemDB, newTestDynamoS3DB} 110 111 // TODO-Klaytn-Database Need to add DynamoDB to the below list. 112 testDatabases := []func() (Database, func()){newTestLDB, newTestBadgerDB, newTestMemDB} 113 for _, newFn := range testDatabases { 114 db, remove := newFn() 115 suite.Run(t, &commonDatabaseTestSuite{database: db}) 116 remove() 117 } 118 } 119 120 // TestNilValue checks if all database write/read nil value in the same way. 121 func (ts *commonDatabaseTestSuite) TestNilValue() { 122 db, t := ts.database, ts.T() 123 124 // non-batch 125 { 126 // write nil value 127 key := common.MakeRandomBytes(32) 128 assert.Nil(t, db.Put(key, nil)) 129 130 // get nil value 131 ret, err := db.Get(key) 132 assert.Equal(t, []byte{}, ret) 133 assert.Nil(t, err) 134 135 // check existence 136 exist, err := db.Has(key) 137 assert.Equal(t, true, exist) 138 assert.Nil(t, err) 139 140 val, err := db.Get(randStrBytes(100)) 141 assert.Nil(t, val) 142 assert.Error(t, err) 143 assert.Equal(t, dataNotFoundErr, err) 144 } 145 146 // batch 147 { 148 batch := db.NewBatch() 149 150 // write nil value 151 key := common.MakeRandomBytes(32) 152 assert.Nil(t, batch.Put(key, nil)) 153 assert.NoError(t, batch.Write()) 154 155 // get nil value 156 ret, err := db.Get(key) 157 assert.Equal(t, []byte{}, ret) 158 assert.Nil(t, err) 159 160 // check existence 161 exist, err := db.Has(key) 162 assert.Equal(t, true, exist) 163 assert.Nil(t, err) 164 165 val, err := db.Get(randStrBytes(100)) 166 assert.Nil(t, val) 167 assert.Error(t, err) 168 assert.Equal(t, dataNotFoundErr, err) 169 } 170 } 171 172 // TestNotFoundErr checks if an empty database returns DataNotFoundErr for the given random key. 173 func (ts *commonDatabaseTestSuite) TestNotFoundErr() { 174 db, t := ts.database, ts.T() 175 176 val, err := db.Get(randStrBytes(100)) 177 assert.Nil(t, val) 178 assert.Error(t, err) 179 assert.Equal(t, dataNotFoundErr, err) 180 } 181 182 // TestPutGet tests the basic put and get operations. 183 func (ts *commonDatabaseTestSuite) TestPutGet() { 184 db, t := ts.database, ts.T() 185 186 // Since badgerDB can't store empty key, testValues is modified. Below line is the original testValues. 187 // var testValues = []string{"", "a", "1251", "\x00123\x00"} 188 testValues := []string{"a", "1251", "\x00123\x00"} 189 190 // put 191 for _, v := range testValues { 192 err := db.Put([]byte(v), []byte(v)) 193 if err != nil { 194 t.Fatalf("put failed: %v", err) 195 } 196 } 197 198 // get 199 for _, v := range testValues { 200 data, err := db.Get([]byte(v)) 201 if err != nil { 202 t.Fatalf("get failed: %v", err) 203 } 204 if !bytes.Equal(data, []byte(v)) { 205 t.Fatalf("get returned wrong result, got %q expected %q", string(data), v) 206 } 207 } 208 209 // override with "?" 210 for _, v := range testValues { 211 err := db.Put([]byte(v), []byte("?")) 212 if err != nil { 213 t.Fatalf("put override failed: %v", err) 214 } 215 } 216 217 // get "?" by key 218 for _, v := range testValues { 219 data, err := db.Get([]byte(v)) 220 if err != nil { 221 t.Fatalf("get failed: %v", err) 222 } 223 if !bytes.Equal(data, []byte("?")) { 224 t.Fatalf("get returned wrong result, got %q expected ?", string(data)) 225 } 226 } 227 228 // override returned value 229 for _, v := range testValues { 230 orig, err := db.Get([]byte(v)) 231 if err != nil { 232 t.Fatalf("get failed: %v", err) 233 } 234 orig[0] = byte(0xff) 235 data, err := db.Get([]byte(v)) 236 if err != nil { 237 t.Fatalf("get failed: %v", err) 238 } 239 if !bytes.Equal(data, []byte("?")) { 240 t.Fatalf("get returned wrong result, got %q expected ?", string(data)) 241 } 242 } 243 244 // delete 245 for _, v := range testValues { 246 err := db.Delete([]byte(v)) 247 if err != nil { 248 t.Fatalf("delete %q failed: %v", v, err) 249 } 250 } 251 252 // try to get deleted values 253 for _, v := range testValues { 254 _, err := db.Get([]byte(v)) 255 if err == nil { 256 t.Fatalf("got deleted value %q", v) 257 } 258 } 259 } 260 261 func TestShardDB(t *testing.T) { 262 key := common.Hex2Bytes("0x91d6f7d2537d8a0bd7d487dcc59151ebc00da306") 263 264 hashstring := strings.TrimPrefix("0x93d6f3d2537d8a0bd7d485dcc59151ebc00da306", "0x") 265 if len(hashstring) > 15 { 266 hashstring = hashstring[:15] 267 } 268 seed, _ := strconv.ParseInt(hashstring, 16, 64) 269 270 shard := seed % int64(12) 271 272 idx := common.BytesToHash(key).Big().Mod(common.BytesToHash(key).Big(), big.NewInt(4)) 273 274 fmt.Printf("idx %d %d %d\n", idx, shard, seed) 275 } 276 277 // TestParallelPutGet tests the parallel put and get operations. 278 func (ts *commonDatabaseTestSuite) TestParallelPutGet() { 279 db := ts.database 280 const n = 8 281 var pending sync.WaitGroup 282 283 pending.Add(n) 284 for i := 0; i < n; i++ { 285 go func(key string) { 286 defer pending.Done() 287 err := db.Put([]byte(key), []byte("v"+key)) 288 if err != nil { 289 panic("put failed: " + err.Error()) 290 } 291 }(strconv.Itoa(i)) 292 } 293 pending.Wait() 294 295 pending.Add(n) 296 for i := 0; i < n; i++ { 297 go func(key string) { 298 defer pending.Done() 299 data, err := db.Get([]byte(key)) 300 if err != nil { 301 panic("get failed: " + err.Error()) 302 } 303 if !bytes.Equal(data, []byte("v"+key)) { 304 panic(fmt.Sprintf("get failed, got %q expected %q", []byte(data), []byte("v"+key))) 305 } 306 }(strconv.Itoa(i)) 307 } 308 pending.Wait() 309 310 pending.Add(n) 311 for i := 0; i < n; i++ { 312 go func(key string) { 313 defer pending.Done() 314 err := db.Delete([]byte(key)) 315 if err != nil { 316 panic("delete failed: " + err.Error()) 317 } 318 }(strconv.Itoa(i)) 319 } 320 pending.Wait() 321 322 pending.Add(n) 323 for i := 0; i < n; i++ { 324 go func(key string) { 325 defer pending.Done() 326 _, err := db.Get([]byte(key)) 327 if err == nil { 328 panic("got deleted value") 329 } 330 }(strconv.Itoa(i)) 331 } 332 pending.Wait() 333 } 334 335 // TestDBEntryLengthCheck checks if dbDirs and dbConfigRatio are 336 // specified for every DBEntryType. 337 func TestDBEntryLengthCheck(t *testing.T) { 338 dbRatioSum := 0 339 for i := 0; i < int(databaseEntryTypeSize); i++ { 340 if dbBaseDirs[i] == "" { 341 t.Fatalf("Database directory should be specified! index: %v", i) 342 } 343 344 if dbConfigRatio[i] == 0 { 345 t.Fatalf("Database configuration ratio should be specified! index: %v", i) 346 } 347 348 dbRatioSum += dbConfigRatio[i] 349 } 350 351 if dbRatioSum != 100 { 352 t.Fatalf("Sum of database configuration ratio should be 100! actual: %v", dbRatioSum) 353 } 354 }