github.com/iotexproject/iotex-core@v1.14.1-rc1/db/counting_index_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  	"context"
    10  	"fmt"
    11  	"strconv"
    12  	"testing"
    13  
    14  	"github.com/pkg/errors"
    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/log"
    21  	"github.com/iotexproject/iotex-core/testutil"
    22  )
    23  
    24  func TestNewCountingIndex(t *testing.T) {
    25  	require := require.New(t)
    26  
    27  	_, err := NewCountingIndexNX(nil, []byte{1})
    28  	require.Equal(ErrInvalid, errors.Cause(err))
    29  	_, err = NewCountingIndexNX(NewMemKVStore(), nil)
    30  	require.Equal(ErrInvalid, errors.Cause(err))
    31  	_, err = NewCountingIndexNX(NewMemKVStore(), []byte{})
    32  	require.Equal(ErrInvalid, errors.Cause(err))
    33  }
    34  
    35  func TestCountingIndex(t *testing.T) {
    36  	testFunc := func(kv KVStore, t *testing.T) {
    37  		require := require.New(t)
    38  
    39  		require.NoError(kv.Start(context.Background()))
    40  		defer func() {
    41  			require.NoError(kv.Stop(context.Background()))
    42  		}()
    43  
    44  		bucket := []byte("test")
    45  		_, err := GetCountingIndex(kv, bucket)
    46  		require.Equal(ErrNotExist, errors.Cause(err))
    47  
    48  		index, err := NewCountingIndexNX(kv, bucket)
    49  		require.NoError(err)
    50  		require.Equal(uint64(0), index.Size())
    51  
    52  		// write 200 entries in batch mode
    53  		for i := 0; i < 200; i++ {
    54  			h := hash.Hash160b([]byte(strconv.Itoa(i)))
    55  			require.NoError(index.Add(h[:], true))
    56  		}
    57  		// cannot Add() before Commit() in batch mode
    58  		require.Equal(ErrInvalid, errors.Cause(index.Add([]byte{1}, false)))
    59  		require.NoError(index.Commit())
    60  		require.EqualValues(200, index.Size())
    61  		// cannot get > size
    62  		_, err = index.Get(index.Size())
    63  		require.Equal(ErrNotExist, errors.Cause(err))
    64  		k, err := index.Get(10)
    65  		require.NoError(err)
    66  		h := hash.Hash160b([]byte(strconv.Itoa(10)))
    67  		require.Equal(h[:], k)
    68  		index.Close()
    69  
    70  		// re-open the bucket
    71  		index, err = GetCountingIndex(kv, bucket)
    72  		require.NoError(err)
    73  		// write another 100 entries
    74  		for i := 200; i < 250; i++ {
    75  			h := hash.Hash160b([]byte(strconv.Itoa(i)))
    76  			require.NoError(index.Add(h[:], false))
    77  		}
    78  		require.EqualValues(250, index.Size())
    79  
    80  		// use external batch
    81  		require.Equal(ErrInvalid, index.Finalize())
    82  		b := batch.NewBatch()
    83  		require.NoError(index.UseBatch(b))
    84  		for i := 250; i < 300; i++ {
    85  			h := hash.Hash160b([]byte(strconv.Itoa(i)))
    86  			require.NoError(index.Add(h[:], true))
    87  		}
    88  		require.NoError(index.Finalize())
    89  		cIndex, ok := index.(*countingIndex)
    90  		require.True(ok)
    91  		require.NoError(cIndex.kvStore.WriteBatch(b))
    92  		require.EqualValues(300, index.Size())
    93  
    94  		_, err = index.Range(248, 0)
    95  		require.Equal(ErrInvalid, errors.Cause(err))
    96  		_, err = index.Range(248, 53)
    97  		require.Equal(ErrInvalid, errors.Cause(err))
    98  
    99  		// last key
   100  		v, err := index.Range(299, 1)
   101  		require.NoError(err)
   102  		require.Equal(1, len(v))
   103  		h = hash.Hash160b([]byte(strconv.Itoa(299)))
   104  		require.Equal(h[:], v[0])
   105  
   106  		// first 5 keys
   107  		v, err = index.Range(0, 5)
   108  		require.NoError(err)
   109  		require.Equal(5, len(v))
   110  		for i := range v {
   111  			h := hash.Hash160b([]byte(strconv.Itoa(i)))
   112  			require.Equal(h[:], v[i])
   113  		}
   114  
   115  		// last 40 keys
   116  		v, err = index.Range(260, 40)
   117  		require.NoError(err)
   118  		require.Equal(40, len(v))
   119  		for i := range v {
   120  			h := hash.Hash160b([]byte(strconv.Itoa(260 + i)))
   121  			require.Equal(h[:], v[i])
   122  		}
   123  		index.Close()
   124  
   125  		// re-open the bucket, verify size = 300
   126  		index1, err := GetCountingIndex(kv, bucket)
   127  		require.NoError(err)
   128  		require.EqualValues(300, index1.Size())
   129  
   130  		// revert last 40 keys
   131  		err = index1.Revert(0)
   132  		require.Equal(ErrInvalid, errors.Cause(err))
   133  		err = index1.Revert(index1.Size() + 1)
   134  		require.Equal(ErrInvalid, errors.Cause(err))
   135  		require.NoError(index1.Revert(40))
   136  		require.EqualValues(260, index1.Size())
   137  
   138  		// last 40 keys
   139  		_, err = index1.Range(220, 41)
   140  		require.Equal(ErrInvalid, errors.Cause(err))
   141  		v, err = index1.Range(220, 40)
   142  		require.NoError(err)
   143  		require.Equal(40, len(v))
   144  		for i := range v {
   145  			h := hash.Hash160b([]byte(strconv.Itoa(220 + i)))
   146  			require.Equal(h[:], v[i])
   147  		}
   148  	}
   149  
   150  	path := "test-counting.bolt"
   151  	testPath, err := testutil.PathOfTempFile(path)
   152  	require.NoError(t, err)
   153  	defer testutil.CleanupPath(testPath)
   154  	cfg := DefaultConfig
   155  	cfg.DbPath = testPath
   156  
   157  	for _, v := range []KVStore{
   158  		NewMemKVStore(),
   159  		NewBoltDB(cfg),
   160  	} {
   161  		t.Run("test counting index", func(t *testing.T) {
   162  			testFunc(v, t)
   163  		})
   164  	}
   165  }
   166  
   167  const (
   168  	Tenants = 10000
   169  	Keys    = 200
   170  )
   171  
   172  func TestBulk(t *testing.T) {
   173  	if testing.Short() {
   174  		t.Skip("skipping test in short mode.")
   175  	}
   176  
   177  	testFunc := func(kv KVStore, t *testing.T) {
   178  		require := require.New(t)
   179  
   180  		require.NoError(kv.Start(context.Background()))
   181  		defer func() {
   182  			require.NoError(kv.Stop(context.Background()))
   183  		}()
   184  
   185  		// create 10000 tenants
   186  		for i := 0; i < Tenants; i++ {
   187  			h := hash.Hash160b([]byte(strconv.Itoa(i)))
   188  			tenant, err := NewCountingIndexNX(kv, h[:])
   189  			require.NoError(err)
   190  
   191  			for i := 0; i < Keys; i++ {
   192  				h := hash.Hash160b([]byte(strconv.Itoa(i)))
   193  				require.NoError(tenant.Add(h[:], true))
   194  			}
   195  			require.NoError(tenant.Commit())
   196  			tenant.Close()
   197  			log.L().Info(fmt.Sprintf("write tenant %d:\n", i))
   198  		}
   199  	}
   200  
   201  	cfg := DefaultConfig
   202  	cfg.DbPath = "test-bulk.dat"
   203  	t.Run("Bolt DB", func(t *testing.T) {
   204  		testutil.CleanupPath(cfg.DbPath)
   205  		testFunc(NewBoltDB(cfg), t)
   206  	})
   207  }
   208  
   209  func TestCheckBulk(t *testing.T) {
   210  	if testing.Short() {
   211  		t.Skip("skipping test in short mode.")
   212  	}
   213  
   214  	testFunc := func(kv KVStore, t *testing.T) {
   215  		require := require.New(t)
   216  
   217  		require.NoError(kv.Start(context.Background()))
   218  		defer func() {
   219  			require.NoError(kv.Stop(context.Background()))
   220  		}()
   221  
   222  		// verify 1000 tenants
   223  		for i := 0; i < Tenants; i++ {
   224  			h := hash.Hash160b([]byte(strconv.Itoa(i)))
   225  			index, err := GetCountingIndex(kv, h[:])
   226  			require.NoError(err)
   227  			require.EqualValues(Keys, index.Size())
   228  
   229  			value, err := index.Range(0, Keys)
   230  			require.NoError(err)
   231  			require.EqualValues(Keys, len(value))
   232  
   233  			for i := range value {
   234  				h := hash.Hash160b([]byte(strconv.Itoa(i)))
   235  				require.Equal(h[:], value[i])
   236  			}
   237  			log.L().Info(fmt.Sprintf("verify tenant: %d\n", i))
   238  		}
   239  	}
   240  
   241  	cfg := DefaultConfig
   242  	cfg.DbPath = "test-bulk.dat"
   243  	t.Run("Bolt DB", func(t *testing.T) {
   244  		defer testutil.CleanupPath(cfg.DbPath)
   245  		testFunc(NewBoltDB(cfg), t)
   246  	})
   247  }