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  }