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 }