github.com/klaytn/klaytn@v1.12.1/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 "math/big" 27 "os" 28 "sort" 29 "strconv" 30 "strings" 31 "sync" 32 "testing" 33 34 "github.com/klaytn/klaytn/common" 35 "github.com/klaytn/klaytn/log" 36 "github.com/stretchr/testify/assert" 37 "github.com/stretchr/testify/suite" 38 ) 39 40 func newTestLDB() (Database, func(), string) { 41 dirName, err := os.MkdirTemp(os.TempDir(), "klay_leveldb_test_") 42 if err != nil { 43 panic("failed to create test file: " + err.Error()) 44 } 45 db, err := NewLevelDBWithOption(dirName, GetDefaultLevelDBOption()) 46 if err != nil { 47 panic("failed to create test database: " + err.Error()) 48 } 49 50 return db, func() { 51 db.Close() 52 os.RemoveAll(dirName) 53 }, "ldb" 54 } 55 56 func newTestBadgerDB() (Database, func(), string) { 57 dirName, err := os.MkdirTemp(os.TempDir(), "klay_badgerdb_test_") 58 if err != nil { 59 panic("failed to create test file: " + err.Error()) 60 } 61 db, err := NewBadgerDB(dirName) 62 if err != nil { 63 panic("failed to create test database: " + err.Error()) 64 } 65 66 return db, func() { 67 db.Close() 68 os.RemoveAll(dirName) 69 }, "badger" 70 } 71 72 func newTestMemDB() (Database, func(), string) { 73 db := NewMemDB() 74 return db, func() { 75 db.Close() 76 }, "memdb" 77 } 78 79 func newTestDynamoS3DB() (Database, func(), string) { 80 // to start test with DynamoDB singletons 81 oldDynamoDBClient := dynamoDBClient 82 dynamoDBClient = nil 83 84 oldDynamoOnceWorker := dynamoOnceWorker 85 dynamoOnceWorker = &sync.Once{} 86 87 oldDynamoWriteCh := dynamoWriteCh 88 dynamoWriteCh = nil 89 90 db, err := newDynamoDB(GetTestDynamoConfig()) 91 if err != nil { 92 panic("failed to create test DynamoS3 database: " + err.Error()) 93 } 94 return db, func() { 95 db.Close() 96 db.deleteTable() 97 db.fdb.deleteBucket() 98 99 // to finish test with DynamoDB singletons 100 dynamoDBClient = oldDynamoDBClient 101 dynamoOnceWorker = oldDynamoOnceWorker 102 dynamoWriteCh = oldDynamoWriteCh 103 }, "dynamos3db" 104 } 105 106 type commonDatabaseTestSuite struct { 107 suite.Suite 108 newFn func() (Database, func(), string) 109 removeFn func() 110 database Database 111 } 112 113 var testDatabases []func() (Database, func(), string) 114 115 func TestDatabaseTestSuite(t *testing.T) { 116 // If you want to include dynamo test, use below line 117 // var testDatabases = []func() (Database, func()){newTestLDB, newTestBadgerDB, newTestMemDB, newTestDynamoS3DB} 118 119 // TODO-Klaytn-Database Need to add DynamoDB to the below list. 120 testDatabases = append(testDatabases, newTestLDB, newTestBadgerDB, newTestMemDB) 121 for _, newFn := range testDatabases { 122 suite.Run(t, &commonDatabaseTestSuite{newFn: newFn}) 123 } 124 } 125 126 func (ts *commonDatabaseTestSuite) BeforeTest(suiteName, testName string) { 127 var dbname string 128 ts.database, ts.removeFn, dbname = ts.newFn() 129 ts.T().Logf("before test - dbname: %v, suiteName: %v, testName: %v", dbname, suiteName, testName) 130 } 131 132 func (ts *commonDatabaseTestSuite) AfterTest(suiteName, testName string) { 133 ts.T().Logf("after test - suiteName: %v, testName: %v", suiteName, testName) 134 ts.removeFn() 135 ts.database, ts.removeFn = nil, nil 136 } 137 138 // TestNilValue checks if all database write/read nil value in the same way. 139 func (ts *commonDatabaseTestSuite) TestNilValue() { 140 db, t := ts.database, ts.T() 141 142 // non-batch 143 { 144 // write nil value 145 key := common.MakeRandomBytes(32) 146 assert.Nil(t, db.Put(key, nil)) 147 148 // get nil value 149 ret, err := db.Get(key) 150 assert.Equal(t, []byte{}, ret) 151 assert.Nil(t, err) 152 153 // check existence 154 exist, err := db.Has(key) 155 assert.Equal(t, true, exist) 156 assert.Nil(t, err) 157 158 val, err := db.Get(randStrBytes(100)) 159 assert.Nil(t, val) 160 assert.Error(t, err) 161 assert.Equal(t, dataNotFoundErr, err) 162 } 163 164 // batch 165 { 166 batch := db.NewBatch() 167 168 // write nil value 169 key := common.MakeRandomBytes(32) 170 assert.Nil(t, batch.Put(key, nil)) 171 assert.NoError(t, batch.Write()) 172 173 // get nil value 174 ret, err := db.Get(key) 175 assert.Equal(t, []byte{}, ret) 176 assert.Nil(t, err) 177 178 // check existence 179 exist, err := db.Has(key) 180 assert.Equal(t, true, exist) 181 assert.Nil(t, err) 182 183 val, err := db.Get(randStrBytes(100)) 184 assert.Nil(t, val) 185 assert.Error(t, err) 186 assert.Equal(t, dataNotFoundErr, err) 187 } 188 } 189 190 // TestNotFoundErr checks if an empty database returns DataNotFoundErr for the given random key. 191 func (ts *commonDatabaseTestSuite) TestNotFoundErr() { 192 db, t := ts.database, ts.T() 193 194 val, err := db.Get(randStrBytes(100)) 195 assert.Nil(t, val) 196 assert.Error(t, err) 197 assert.Equal(t, dataNotFoundErr, err) 198 } 199 200 // TestPutGet tests the basic put and get operations. 201 func (ts *commonDatabaseTestSuite) TestPutGet() { 202 db, t := ts.database, ts.T() 203 204 // Since badgerDB can't store empty key, testValues is modified. Below line is the original testValues. 205 // var testValues = []string{"", "a", "1251", "\x00123\x00"} 206 testValues := []string{"a", "1251", "\x00123\x00"} 207 208 // put 209 for _, v := range testValues { 210 err := db.Put([]byte(v), []byte(v)) 211 if err != nil { 212 t.Fatalf("put failed: %v", err) 213 } 214 } 215 216 // get 217 for _, v := range testValues { 218 data, err := db.Get([]byte(v)) 219 if err != nil { 220 t.Fatalf("get failed: %v", err) 221 } 222 if !bytes.Equal(data, []byte(v)) { 223 t.Fatalf("get returned wrong result, got %q expected %q", string(data), v) 224 } 225 } 226 227 // override with "?" 228 for _, v := range testValues { 229 err := db.Put([]byte(v), []byte("?")) 230 if err != nil { 231 t.Fatalf("put override failed: %v", err) 232 } 233 } 234 235 // get "?" by key 236 for _, v := range testValues { 237 data, err := db.Get([]byte(v)) 238 if err != nil { 239 t.Fatalf("get failed: %v", err) 240 } 241 if !bytes.Equal(data, []byte("?")) { 242 t.Fatalf("get returned wrong result, got %q expected ?", string(data)) 243 } 244 } 245 246 // override returned value 247 for _, v := range testValues { 248 orig, err := db.Get([]byte(v)) 249 if err != nil { 250 t.Fatalf("get failed: %v", err) 251 } 252 orig[0] = byte(0xff) 253 data, err := db.Get([]byte(v)) 254 if err != nil { 255 t.Fatalf("get failed: %v", err) 256 } 257 if !bytes.Equal(data, []byte("?")) { 258 t.Fatalf("get returned wrong result, got %q expected ?", string(data)) 259 } 260 } 261 262 // delete 263 for _, v := range testValues { 264 err := db.Delete([]byte(v)) 265 if err != nil { 266 t.Fatalf("delete %q failed: %v", v, err) 267 } 268 } 269 270 // try to get deleted values 271 for _, v := range testValues { 272 _, err := db.Get([]byte(v)) 273 if err == nil { 274 t.Fatalf("got deleted value %q", v) 275 } 276 } 277 } 278 279 func TestShardDB(t *testing.T) { 280 key := common.Hex2Bytes("0x91d6f7d2537d8a0bd7d487dcc59151ebc00da306") 281 282 hashstring := strings.TrimPrefix("0x93d6f3d2537d8a0bd7d485dcc59151ebc00da306", "0x") 283 if len(hashstring) > 15 { 284 hashstring = hashstring[:15] 285 } 286 seed, _ := strconv.ParseInt(hashstring, 16, 64) 287 288 shard := seed % int64(12) 289 290 idx := common.BytesToHash(key).Big().Mod(common.BytesToHash(key).Big(), big.NewInt(4)) 291 292 fmt.Printf("idx %d %d %d\n", idx, shard, seed) 293 } 294 295 // TestParallelPutGet tests the parallel put and get operations. 296 func (ts *commonDatabaseTestSuite) TestParallelPutGet() { 297 db := ts.database 298 const n = 8 299 var pending sync.WaitGroup 300 301 pending.Add(n) 302 for i := 0; i < n; i++ { 303 go func(key string) { 304 defer pending.Done() 305 err := db.Put([]byte(key), []byte("v"+key)) 306 if err != nil { 307 panic("put failed: " + err.Error()) 308 } 309 }(strconv.Itoa(i)) 310 } 311 pending.Wait() 312 313 pending.Add(n) 314 for i := 0; i < n; i++ { 315 go func(key string) { 316 defer pending.Done() 317 data, err := db.Get([]byte(key)) 318 if err != nil { 319 panic("get failed: " + err.Error()) 320 } 321 if !bytes.Equal(data, []byte("v"+key)) { 322 panic(fmt.Sprintf("get failed, got %q expected %q", []byte(data), []byte("v"+key))) 323 } 324 }(strconv.Itoa(i)) 325 } 326 pending.Wait() 327 328 pending.Add(n) 329 for i := 0; i < n; i++ { 330 go func(key string) { 331 defer pending.Done() 332 err := db.Delete([]byte(key)) 333 if err != nil { 334 panic("delete failed: " + err.Error()) 335 } 336 }(strconv.Itoa(i)) 337 } 338 pending.Wait() 339 340 pending.Add(n) 341 for i := 0; i < n; i++ { 342 go func(key string) { 343 defer pending.Done() 344 _, err := db.Get([]byte(key)) 345 if err == nil { 346 panic("got deleted value") 347 } 348 }(strconv.Itoa(i)) 349 } 350 pending.Wait() 351 } 352 353 // TestDBEntryLengthCheck checks if dbDirs and dbConfigRatio are 354 // specified for every DBEntryType. 355 func TestDBEntryLengthCheck(t *testing.T) { 356 dbRatioSum := 0 357 for i := 0; i < int(databaseEntryTypeSize); i++ { 358 if dbBaseDirs[i] == "" { 359 t.Fatalf("Database directory should be specified! index: %v", i) 360 } 361 362 if dbConfigRatio[i] == 0 { 363 t.Fatalf("Database configuration ratio should be specified! index: %v", i) 364 } 365 366 dbRatioSum += dbConfigRatio[i] 367 } 368 369 if dbRatioSum != 100 { 370 t.Fatalf("Sum of database configuration ratio should be 100! actual: %v", dbRatioSum) 371 } 372 } 373 374 type testData struct { 375 k, v []byte 376 } 377 378 type testDataSlice []*testData 379 380 func (d testDataSlice) Len() int { 381 return len(d) 382 } 383 384 func (d testDataSlice) Swap(i, j int) { 385 d[i], d[j] = d[j], d[i] 386 } 387 388 func (d testDataSlice) Less(i, j int) bool { 389 return bytes.Compare(d[i].k, d[j].k) < 0 390 } 391 392 func insertRandomData(db KeyValueWriter, prefix []byte, num int) (testDataSlice, error) { 393 ret := testDataSlice{} 394 for i := 0; i < num; i++ { 395 key := common.MakeRandomBytes(32) 396 val := append(key, key...) 397 if len(prefix) > 0 { 398 key = append(prefix, key...) 399 } 400 ret = append(ret, &testData{k: key, v: val}) 401 402 if err := db.Put(key, val); err != nil { 403 return nil, err 404 } 405 } 406 407 return ret, nil 408 } 409 410 func (ts *commonDatabaseTestSuite) Test_Put() { 411 num := 100 412 413 // test put 414 data, err := insertRandomData(ts.database, nil, num) 415 assert.NoError(ts.T(), err) 416 assert.Equal(ts.T(), num, len(data)) 417 } 418 419 func (ts *commonDatabaseTestSuite) Test_Get() { 420 num, db := 100, ts.database 421 422 data, _ := insertRandomData(ts.database, nil, num) 423 424 for idx := range data { 425 actual, err := db.Get(data[idx].k) 426 assert.NoError(ts.T(), err) 427 assert.Equal(ts.T(), data[idx].v, actual) 428 } 429 430 notExistKey := []byte{0x1} 431 actual, err := db.Get(notExistKey) 432 assert.Equal(ts.T(), dataNotFoundErr, err) 433 assert.Nil(ts.T(), actual) 434 } 435 436 func (ts *commonDatabaseTestSuite) Test_Has() { 437 num, db := 100, ts.database 438 439 data, _ := insertRandomData(ts.database, nil, num) 440 441 for idx := range data { 442 has, err := db.Has(data[idx].k) 443 assert.NoError(ts.T(), err) 444 assert.True(ts.T(), has) 445 } 446 447 notExistKey := []byte{0x1} 448 has, err := db.Has(notExistKey) 449 assert.NoError(ts.T(), err) 450 assert.False(ts.T(), has) 451 } 452 453 func (ts *commonDatabaseTestSuite) Test_Delete() { 454 num, db := 100, ts.database 455 456 data, _ := insertRandomData(ts.database, nil, num) 457 458 for idx := range data { 459 if idx%2 == 0 { 460 err := db.Delete(data[idx].k) 461 assert.NoError(ts.T(), err) 462 } 463 } 464 465 for idx := range data { 466 has, _ := db.Has(data[idx].k) 467 if idx%2 == 0 { 468 assert.False(ts.T(), has) 469 } else { 470 assert.True(ts.T(), has) 471 } 472 } 473 } 474 475 func (ts *commonDatabaseTestSuite) Test_Iterator_NoData() { 476 log.EnableLogForTest(log.LvlCrit, log.LvlTrace) 477 db := ts.database 478 if _, ok := db.(*badgerDB); ok { 479 ts.T().Skip() 480 } 481 482 // testing iterator without prefix nor specific-starting key 483 it := db.NewIterator(nil, nil) 484 defer it.Release() 485 486 assert.False(ts.T(), it.Next()) 487 } 488 489 func (ts *commonDatabaseTestSuite) Test_Iterator_WithoutPrefixAndStart() { 490 log.EnableLogForTest(log.LvlCrit, log.LvlTrace) 491 num, db := 100, ts.database 492 if _, ok := db.(*badgerDB); ok { 493 ts.T().Skip() 494 } 495 496 data, _ := insertRandomData(ts.database, nil, num) 497 sort.Sort(data) 498 499 // testing iterator without prefix nor specific-starting key 500 it := db.NewIterator(nil, nil) 501 defer it.Release() 502 503 idx := 0 504 for it.Next() { 505 key, val := it.Key(), it.Value() 506 assert.Equal(ts.T(), data[idx].k, key) 507 assert.Equal(ts.T(), data[idx].v, val) 508 idx++ 509 } 510 assert.Equal(ts.T(), len(data), idx) 511 } 512 513 func (ts *commonDatabaseTestSuite) Test_Iterator_WithPrefix() { 514 log.EnableLogForTest(log.LvlCrit, log.LvlTrace) 515 num, prefix, db := 10, common.Hex2Bytes("deaddeaf"), ts.database 516 if _, ok := db.(*badgerDB); ok { 517 ts.T().Skip() 518 } 519 520 insertRandomData(ts.database, nil, num) 521 prefixData, _ := insertRandomData(ts.database, prefix, num) 522 sort.Sort(prefixData) 523 524 // testing iterator with key-prefix 525 it := db.NewIterator(prefix, nil) 526 defer it.Release() 527 assert.Nil(ts.T(), it.Key()) 528 assert.Nil(ts.T(), it.Value()) 529 530 idx := 0 531 for it.Next() { 532 key, val := it.Key(), it.Value() 533 assert.Equal(ts.T(), prefixData[idx].k, key) 534 assert.Equal(ts.T(), prefixData[idx].v, val) 535 idx++ 536 } 537 assert.Equal(ts.T(), len(prefixData), idx) 538 } 539 540 func (ts *commonDatabaseTestSuite) Test_Iterator_WithStart() { 541 log.EnableLogForTest(log.LvlCrit, log.LvlTrace) 542 num, db := 100, ts.database 543 if _, ok := db.(*badgerDB); ok { 544 ts.T().Skip() 545 } 546 547 data, _ := insertRandomData(ts.database, nil, num) 548 sort.Sort(data) 549 550 startIdx := len(data) / 3 551 552 // testing iterator with specific starting key 553 it := db.NewIterator(nil, data[startIdx].k) 554 defer it.Release() 555 assert.Nil(ts.T(), it.Key()) 556 assert.Nil(ts.T(), it.Value()) 557 558 idx := startIdx 559 for it.Next() { 560 key, val := it.Key(), it.Value() 561 assert.Equal(ts.T(), data[idx].k, key) 562 assert.Equal(ts.T(), data[idx].v, val) 563 idx++ 564 } 565 assert.Equal(ts.T(), len(data), idx) 566 } 567 568 func (ts *commonDatabaseTestSuite) Test_Iterator_WithPrefixAndStart() { 569 log.EnableLogForTest(log.LvlCrit, log.LvlTrace) 570 num, prefix, db := 10, common.Hex2Bytes("deaddeaf"), ts.database 571 if _, ok := db.(*badgerDB); ok { 572 ts.T().Skip() 573 } 574 575 insertRandomData(ts.database, common.Hex2Bytes("aaaabbbb"), num) 576 data, _ := insertRandomData(ts.database, prefix, num) 577 sort.Sort(data) 578 579 startIdx := len(data) / 3 580 581 // testing iterator with prefix and specific-starting key 582 it := db.NewIterator(prefix, data[startIdx].k[4:]) 583 defer it.Release() 584 assert.Nil(ts.T(), it.Key()) 585 assert.Nil(ts.T(), it.Value()) 586 587 idx := startIdx 588 for it.Next() { 589 key, val := it.Key(), it.Value() 590 assert.Equal(ts.T(), data[idx].k, key) 591 assert.Equal(ts.T(), data[idx].v, val) 592 idx++ 593 } 594 assert.Equal(ts.T(), len(data), idx) 595 } 596 597 func (ts *commonDatabaseTestSuite) Test_BatchWrite() { 598 log.EnableLogForTest(log.LvlCrit, log.LvlTrace) 599 numData, numIter, db := 1000, 100, ts.database 600 if _, ok := db.(*badgerDB); ok { 601 ts.T().Skip() 602 } 603 604 batch := db.NewBatch() 605 defer batch.Release() 606 data := testDataSlice{} 607 for i := 0; i < numIter; i++ { 608 inserted, _ := insertRandomData(batch, nil, numData) 609 batch.Write() 610 batch.Reset() 611 data = append(data, inserted...) 612 } 613 614 assert.Equal(ts.T(), numData*numIter, len(data)) 615 for _, d := range data { 616 actual, err := db.Get(d.k) 617 assert.NoError(ts.T(), err) 618 assert.Equal(ts.T(), d.v, actual) 619 } 620 }