github.com/iotexproject/iotex-core@v1.14.1-rc1/db/db_test.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package db
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"testing"
    12  
    13  	"github.com/pkg/errors"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/iotexproject/go-pkgs/hash"
    18  
    19  	"github.com/iotexproject/iotex-core/db/batch"
    20  	"github.com/iotexproject/iotex-core/pkg/util/byteutil"
    21  	"github.com/iotexproject/iotex-core/testutil"
    22  )
    23  
    24  var (
    25  	_bucket1 = "test_ns1"
    26  	_bucket2 = "test_ns2"
    27  	_testK1  = [3][]byte{[]byte("key_1"), []byte("key_2"), []byte("key_3")}
    28  	_testV1  = [3][]byte{[]byte("value_1"), []byte("value_2"), []byte("value_3")}
    29  	_testK2  = [3][]byte{[]byte("key_4"), []byte("key_5"), []byte("key_6")}
    30  	_testV2  = [3][]byte{[]byte("value_4"), []byte("value_5"), []byte("value_6")}
    31  )
    32  
    33  func TestKVStorePutGet(t *testing.T) {
    34  	testKVStorePutGet := func(kvStore KVStore, t *testing.T) {
    35  		assert := assert.New(t)
    36  		ctx := context.Background()
    37  
    38  		assert.Nil(kvStore.Start(ctx))
    39  		defer func() {
    40  			err := kvStore.Stop(ctx)
    41  			assert.Nil(err)
    42  		}()
    43  
    44  		assert.Nil(kvStore.Put(_bucket1, []byte("key"), []byte("value")))
    45  		value, err := kvStore.Get(_bucket1, []byte("key"))
    46  		assert.Nil(err)
    47  		assert.Equal([]byte("value"), value)
    48  		value, err = kvStore.Get("test_ns_1", []byte("key"))
    49  		assert.NotNil(err)
    50  		assert.Nil(value)
    51  		value, err = kvStore.Get(_bucket1, _testK1[0])
    52  		assert.NotNil(err)
    53  		assert.Nil(value)
    54  	}
    55  
    56  	path := "test-kv-store.bolt"
    57  	testPath, err := testutil.PathOfTempFile(path)
    58  	require.NoError(t, err)
    59  	defer testutil.CleanupPath(testPath)
    60  	cfg := DefaultConfig
    61  	cfg.DbPath = testPath
    62  
    63  	for _, v := range []KVStore{
    64  		NewMemKVStore(),
    65  		NewBoltDB(cfg),
    66  	} {
    67  		t.Run("test put get", func(t *testing.T) {
    68  			testKVStorePutGet(v, t)
    69  		})
    70  	}
    71  
    72  }
    73  
    74  func TestBatchRollback(t *testing.T) {
    75  	testBatchRollback := func(kvStore KVStore, t *testing.T) {
    76  		assert := assert.New(t)
    77  		ctx := context.Background()
    78  
    79  		assert.Nil(kvStore.Start(ctx))
    80  		defer func() {
    81  			err := kvStore.Stop(ctx)
    82  			assert.Nil(err)
    83  		}()
    84  
    85  		assert.Nil(kvStore.Put(_bucket1, _testK1[0], _testV1[0]))
    86  		value, err := kvStore.Get(_bucket1, _testK1[0])
    87  		assert.Nil(err)
    88  		assert.Equal(_testV1[0], value)
    89  		assert.Nil(kvStore.Put(_bucket1, _testK1[1], _testV1[1]))
    90  		value, err = kvStore.Get(_bucket1, _testK1[1])
    91  		assert.Nil(err)
    92  		assert.Equal(_testV1[1], value)
    93  		assert.Nil(kvStore.Put(_bucket1, _testK1[2], _testV1[2]))
    94  		value, err = kvStore.Get(_bucket1, _testK1[2])
    95  		assert.Nil(err)
    96  		assert.Equal(_testV1[2], value)
    97  
    98  		testV := [3][]byte{[]byte("value1.1"), []byte("value2.1"), []byte("value3.1")}
    99  		kvboltDB := kvStore.(*BoltDB)
   100  		err = kvboltDB.batchPutForceFail(_bucket1, _testK1[:], testV[:])
   101  		assert.NotNil(err)
   102  
   103  		value, err = kvStore.Get(_bucket1, _testK1[0])
   104  		assert.Nil(err)
   105  		assert.Equal(_testV1[0], value)
   106  		value, err = kvStore.Get(_bucket1, _testK1[1])
   107  		assert.Nil(err)
   108  		assert.Equal(_testV1[1], value)
   109  		value, err = kvStore.Get(_bucket1, _testK1[2])
   110  		assert.Nil(err)
   111  		assert.Equal(_testV1[2], value)
   112  	}
   113  
   114  	path := "test-batch-rollback.bolt"
   115  	testPath, err := testutil.PathOfTempFile(path)
   116  	require.NoError(t, err)
   117  	defer testutil.CleanupPath(testPath)
   118  	cfg := DefaultConfig
   119  	cfg.DbPath = testPath
   120  
   121  	t.Run("test rollback", func(t *testing.T) {
   122  		testBatchRollback(NewBoltDB(cfg), t)
   123  	})
   124  
   125  }
   126  
   127  func TestDBInMemBatchCommit(t *testing.T) {
   128  	require := require.New(t)
   129  	kvStore := NewMemKVStore()
   130  	ctx := context.Background()
   131  	b := batch.NewBatch()
   132  
   133  	require.NoError(kvStore.Start(ctx))
   134  	defer func() {
   135  		require.NoError(kvStore.Stop(ctx))
   136  	}()
   137  
   138  	require.NoError(kvStore.Put(_bucket1, _testK1[0], _testV1[1]))
   139  	require.NoError(kvStore.Put(_bucket2, _testK2[1], _testV2[0]))
   140  	require.NoError(kvStore.Put(_bucket1, _testK1[2], _testV1[0]))
   141  	b.Put(_bucket1, _testK1[0], _testV1[0], "")
   142  	value, err := kvStore.Get(_bucket1, _testK1[0])
   143  	require.NoError(err)
   144  	require.Equal(_testV1[1], value)
   145  	value, err = kvStore.Get(_bucket2, _testK2[1])
   146  	require.NoError(err)
   147  	require.Equal(_testV2[0], value)
   148  	require.NoError(kvStore.WriteBatch(b))
   149  	value, err = kvStore.Get(_bucket1, _testK1[0])
   150  	require.NoError(err)
   151  	require.Equal(_testV1[0], value)
   152  }
   153  
   154  func TestDBBatch(t *testing.T) {
   155  	testBatchRollback := func(kvStore KVStore, t *testing.T) {
   156  		require := require.New(t)
   157  		ctx := context.Background()
   158  		batch := batch.NewBatch()
   159  
   160  		require.NoError(kvStore.Start(ctx))
   161  		defer func() {
   162  			require.NoError(kvStore.Stop(ctx))
   163  		}()
   164  
   165  		require.NoError(kvStore.Put(_bucket1, _testK1[0], _testV1[1]))
   166  		require.NoError(kvStore.Put(_bucket2, _testK2[1], _testV2[0]))
   167  		require.NoError(kvStore.Put(_bucket1, _testK1[2], _testV1[0]))
   168  
   169  		batch.Put(_bucket1, _testK1[0], _testV1[0], "")
   170  		batch.Put(_bucket2, _testK2[1], _testV2[1], "")
   171  		value, err := kvStore.Get(_bucket1, _testK1[0])
   172  		require.NoError(err)
   173  		require.Equal(_testV1[1], value)
   174  
   175  		value, err = kvStore.Get(_bucket2, _testK2[1])
   176  		require.NoError(err)
   177  		require.Equal(_testV2[0], value)
   178  		require.NoError(kvStore.WriteBatch(batch))
   179  		batch.Clear()
   180  
   181  		value, err = kvStore.Get(_bucket1, _testK1[0])
   182  		require.NoError(err)
   183  		require.Equal(_testV1[0], value)
   184  
   185  		value, err = kvStore.Get(_bucket2, _testK2[1])
   186  		require.NoError(err)
   187  		require.Equal(_testV2[1], value)
   188  
   189  		value, err = kvStore.Get(_bucket1, _testK1[2])
   190  		require.NoError(err)
   191  		require.Equal(_testV1[0], value)
   192  
   193  		batch.Put(_bucket1, _testK1[0], _testV1[1], "")
   194  		require.NoError(kvStore.WriteBatch(batch))
   195  		batch.Clear()
   196  
   197  		require.Equal(0, batch.Size())
   198  
   199  		value, err = kvStore.Get(_bucket2, _testK2[1])
   200  		require.NoError(err)
   201  		require.Equal(_testV2[1], value)
   202  
   203  		value, err = kvStore.Get(_bucket1, _testK1[0])
   204  		require.NoError(err)
   205  		require.Equal(_testV1[1], value)
   206  
   207  		require.NoError(kvStore.WriteBatch(batch))
   208  		batch.Clear()
   209  
   210  		batch.Put(_bucket1, _testK1[2], _testV1[2], "")
   211  		require.NoError(kvStore.WriteBatch(batch))
   212  		batch.Clear()
   213  
   214  		value, err = kvStore.Get(_bucket1, _testK1[2])
   215  		require.NoError(err)
   216  		require.Equal(_testV1[2], value)
   217  
   218  		value, err = kvStore.Get(_bucket2, _testK2[1])
   219  		require.NoError(err)
   220  		require.Equal(_testV2[1], value)
   221  
   222  		batch.Clear()
   223  		batch.Put(_bucket1, _testK1[2], _testV1[2], "")
   224  		batch.Delete(_bucket2, _testK2[1], "")
   225  		require.NoError(kvStore.WriteBatch(batch))
   226  		batch.Clear()
   227  
   228  		value, err = kvStore.Get(_bucket1, _testK1[2])
   229  		require.NoError(err)
   230  		require.Equal(_testV1[2], value)
   231  
   232  		_, err = kvStore.Get(_bucket2, _testK2[1])
   233  		require.Error(err)
   234  	}
   235  
   236  	path := "test-batch-commit.bolt"
   237  	testPath, err := testutil.PathOfTempFile(path)
   238  	require.NoError(t, err)
   239  	defer testutil.CleanupPath(testPath)
   240  	cfg := DefaultConfig
   241  	cfg.DbPath = testPath
   242  
   243  	for _, v := range []KVStore{
   244  		NewMemKVStore(),
   245  		NewBoltDB(cfg),
   246  	} {
   247  		t.Run("test batch", func(t *testing.T) {
   248  			testBatchRollback(v, t)
   249  		})
   250  	}
   251  }
   252  
   253  func TestCacheKV(t *testing.T) {
   254  	testFunc := func(kv KVStore, t *testing.T) {
   255  		require := require.New(t)
   256  
   257  		require.NoError(kv.Start(context.Background()))
   258  		defer func() {
   259  			require.NoError(kv.Stop(context.Background()))
   260  		}()
   261  
   262  		cb := batch.NewCachedBatch()
   263  		cb.Put(_bucket1, _testK1[0], _testV1[0], "")
   264  		v, _ := cb.Get(_bucket1, _testK1[0])
   265  		require.Equal(_testV1[0], v)
   266  		cb.Clear()
   267  		require.Equal(0, cb.Size())
   268  		_, err := cb.Get(_bucket1, _testK1[0])
   269  		require.Error(err)
   270  		cb.Put(_bucket2, _testK2[2], _testV2[2], "")
   271  		v, _ = cb.Get(_bucket2, _testK2[2])
   272  		require.Equal(_testV2[2], v)
   273  		// put _testK1[1] with a new value
   274  		cb.Put(_bucket1, _testK1[1], _testV1[2], "")
   275  		v, _ = cb.Get(_bucket1, _testK1[1])
   276  		require.Equal(_testV1[2], v)
   277  		// delete a non-existing entry is OK
   278  		cb.Delete(_bucket2, []byte("notexist"), "")
   279  		require.NoError(kv.WriteBatch(cb))
   280  
   281  		v, _ = kv.Get(_bucket1, _testK1[1])
   282  		require.Equal(_testV1[2], v)
   283  
   284  		cb = batch.NewCachedBatch()
   285  		require.NoError(kv.WriteBatch(cb))
   286  	}
   287  
   288  	path := "test-cache-kv.bolt"
   289  	testPath, err := testutil.PathOfTempFile(path)
   290  	require.NoError(t, err)
   291  	defer testutil.CleanupPath(testPath)
   292  	cfg := DefaultConfig
   293  	cfg.DbPath = testPath
   294  
   295  	for _, v := range []KVStore{
   296  		NewMemKVStore(),
   297  		NewBoltDB(cfg),
   298  	} {
   299  		t.Run("test cache kv", func(t *testing.T) {
   300  			testFunc(v, t)
   301  		})
   302  	}
   303  }
   304  
   305  func TestDeleteBucket(t *testing.T) {
   306  	testFunc := func(kv KVStore, t *testing.T) {
   307  		require := require.New(t)
   308  
   309  		require.NoError(kv.Start(context.Background()))
   310  		defer func() {
   311  			require.NoError(kv.Stop(context.Background()))
   312  		}()
   313  
   314  		require.NoError(kv.Put(_bucket1, _testK1[0], _testV1[0]))
   315  		v, err := kv.Get(_bucket1, _testK1[0])
   316  		require.NoError(err)
   317  		require.Equal(_testV1[0], v)
   318  
   319  		require.NoError(kv.Put(_bucket2, _testK1[0], _testV1[0]))
   320  		v, err = kv.Get(_bucket2, _testK1[0])
   321  		require.NoError(err)
   322  		require.Equal(_testV1[0], v)
   323  
   324  		require.NoError(kv.Delete(_bucket1, nil))
   325  		v, err = kv.Get(_bucket1, _testK1[0])
   326  		require.Equal(ErrNotExist, errors.Cause(err))
   327  		require.Equal([]uint8([]byte(nil)), v)
   328  
   329  		v, _ = kv.Get(_bucket2, _testK1[0])
   330  		require.Equal(_testV1[0], v)
   331  	}
   332  
   333  	path := "test-delete.bolt"
   334  	testPath, err := testutil.PathOfTempFile(path)
   335  	require.NoError(t, err)
   336  	defer testutil.CleanupPath(testPath)
   337  	cfg := DefaultConfig
   338  	cfg.DbPath = testPath
   339  
   340  	t.Run("test delete bucket", func(t *testing.T) {
   341  		testFunc(NewBoltDB(cfg), t)
   342  	})
   343  }
   344  
   345  func TestFilter(t *testing.T) {
   346  	require := require.New(t)
   347  
   348  	testFunc := func(kv KVStore, t *testing.T) {
   349  		require.NoError(kv.Start(context.Background()))
   350  		defer func() {
   351  			require.NoError(kv.Stop(context.Background()))
   352  		}()
   353  
   354  		tests := []struct {
   355  			ns     string
   356  			prefix []byte
   357  		}{
   358  			{
   359  				_bucket1,
   360  				[]byte("test"),
   361  			},
   362  			{
   363  				_bucket1,
   364  				[]byte("come"),
   365  			},
   366  			{
   367  				_bucket2,
   368  				[]byte("back"),
   369  			},
   370  		}
   371  
   372  		// add 100 keys with each prefix
   373  		b := batch.NewBatch()
   374  		numKey := 100
   375  		for _, e := range tests {
   376  			for i := 0; i < numKey; i++ {
   377  				k := append(e.prefix, byteutil.Uint64ToBytesBigEndian(uint64(i))...)
   378  				v := hash.Hash256b(k)
   379  				b.Put(e.ns, k, v[:], "")
   380  			}
   381  		}
   382  		require.NoError(kv.WriteBatch(b))
   383  
   384  		_, _, err := kv.Filter("nonamespace", func(k, v []byte) bool {
   385  			return bytes.HasPrefix(k, v)
   386  		}, nil, nil)
   387  		require.Equal(ErrBucketNotExist, errors.Cause(err))
   388  
   389  		// filter using func with no match
   390  		fk, fv, err := kv.Filter(tests[0].ns, func(k, v []byte) bool {
   391  			return bytes.HasPrefix(k, tests[2].prefix)
   392  		}, nil, nil)
   393  		require.Nil(fk)
   394  		require.Nil(fv)
   395  		require.Equal(ErrNotExist, errors.Cause(err))
   396  
   397  		// filter out <k, v> pairs
   398  		for _, e := range tests {
   399  			fk, fv, err := kv.Filter(e.ns, func(k, v []byte) bool {
   400  				return bytes.HasPrefix(k, e.prefix)
   401  			}, nil, nil)
   402  			require.NoError(err)
   403  			require.Equal(numKey, len(fk))
   404  			require.Equal(numKey, len(fv))
   405  			for i := range fk {
   406  				k := append(e.prefix, byteutil.Uint64ToBytesBigEndian(uint64(i))...)
   407  				require.Equal(fk[i], k)
   408  				v := hash.Hash256b(k)
   409  				require.Equal(fv[i], v[:])
   410  			}
   411  		}
   412  
   413  		// filter with min/max key
   414  		for _, e := range tests {
   415  			min := 9
   416  			max := 91
   417  			minKey := append(e.prefix, byteutil.Uint64ToBytesBigEndian(uint64(min))...)
   418  			maxKey := append(e.prefix, byteutil.Uint64ToBytesBigEndian(uint64(max))...)
   419  			fk, fv, err := kv.Filter(e.ns, func(k, v []byte) bool {
   420  				return bytes.HasPrefix(k, e.prefix)
   421  			}, minKey, maxKey)
   422  			require.NoError(err)
   423  			require.Equal(max-min+1, len(fk))
   424  			require.Equal(max-min+1, len(fv))
   425  			for i := range fk {
   426  				k := append(e.prefix, byteutil.Uint64ToBytesBigEndian(uint64(i+min))...)
   427  				require.Equal(fk[i], k)
   428  				v := hash.Hash256b(k)
   429  				require.Equal(fv[i], v[:])
   430  			}
   431  		}
   432  	}
   433  
   434  	path := "test-filter.bolt"
   435  	testPath, err := testutil.PathOfTempFile(path)
   436  	require.NoError(err)
   437  	defer testutil.CleanupPath(testPath)
   438  	cfg := DefaultConfig
   439  	cfg.DbPath = testPath
   440  
   441  	t.Run("test filter", func(t *testing.T) {
   442  		testFunc(NewBoltDB(cfg), t)
   443  	})
   444  }
   445  
   446  func TestCreateKVStore(t *testing.T) {
   447  	require := require.New(t)
   448  
   449  	path := "test-kv-db.bolt"
   450  	testPath, err := testutil.PathOfTempFile(path)
   451  	require.NoError(err)
   452  	defer testutil.CleanupPath(testPath)
   453  	cfg := DefaultConfig
   454  
   455  	d, err := CreateKVStore(cfg, testPath)
   456  	require.NoError(err)
   457  	require.NotNil(d)
   458  
   459  	d, err = CreateKVStore(cfg, "")
   460  	require.ErrorIs(err, ErrEmptyDBPath)
   461  	require.Nil(d)
   462  
   463  	d, err = CreateKVStoreWithCache(cfg, testPath, 5)
   464  	require.NoError(err)
   465  	require.NotNil(d)
   466  }