Skip to content

Commit 3033a56

Browse files
authored
Cache LevelDB snapshots and use lock-free map (#490)
- Replacing map with sync.Map - Caching DB snapshot after commit to avoid creating a snapshot for each Get operation - Modifying cache to return byte array to avoid code duplication - Remove Db lock because levelDB is already protected - Reduce code duplication in Open() Signed-off-by: Liran Funaro <liran.funaro@gmail.com> --------- Signed-off-by: Liran Funaro <liran.funaro@gmail.com>
1 parent 0ff0eba commit 3033a56

File tree

9 files changed

+228
-234
lines changed

9 files changed

+228
-234
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ require (
1919
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
2020
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2
2121
go.etcd.io/etcd v0.5.0-alpha.5.0.20210226220824-aa7126864d82 // indirect git tag v3.4.15
22+
go.uber.org/multierr v1.6.0
2223
go.uber.org/zap v1.18.1
2324
google.golang.org/protobuf v1.26.0
2425
gopkg.in/yaml.v2 v2.4.0

internal/worldstate/leveldb/cache.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package leveldb
22

33
import (
44
"github.com/VictoriaMetrics/fastcache"
5-
"github.com/golang/protobuf/proto"
6-
"github.com/hyperledger-labs/orion-server/pkg/types"
75
)
86

97
var keySep = []byte{0x00}
@@ -27,19 +25,9 @@ func newCache(dataCacheSizeMBs int) *cache {
2725

2826
// getState returns the value for a given namespace and key from
2927
// the cache
30-
func (c *cache) getState(namespace, key string) (*types.ValueWithMetadata, error) {
28+
func (c *cache) getState(namespace, key string) ([]byte, bool) {
3129
cacheKey := constructCacheKey(namespace, key)
32-
33-
valBytes, exists := c.dataCache.HasGet(nil, cacheKey)
34-
if !exists {
35-
return nil, nil
36-
}
37-
38-
cacheValue := &types.ValueWithMetadata{}
39-
if err := proto.Unmarshal(valBytes, cacheValue); err != nil {
40-
return nil, err
41-
}
42-
return cacheValue, nil
30+
return c.dataCache.HasGet(nil, cacheKey)
4331
}
4432

4533
// putState stores a given value in the cache

internal/worldstate/leveldb/cache_test.go

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ import (
99
"google.golang.org/protobuf/proto"
1010
)
1111

12+
func unmarshaler(t *testing.T) func(b []byte, inCache bool) (*types.ValueWithMetadata, bool) {
13+
return func(b []byte, inCache bool) (*types.ValueWithMetadata, bool) {
14+
if !inCache {
15+
return nil, inCache
16+
}
17+
value := &types.ValueWithMetadata{}
18+
require.NoError(t, proto.Unmarshal(b, value))
19+
return value, inCache
20+
}
21+
}
22+
1223
func TestCache(t *testing.T) {
1324
cache := newCache(10)
1425

@@ -34,8 +45,8 @@ func TestCache(t *testing.T) {
3445

3546
t.Run("check basic storage and retrieval", func(t *testing.T) {
3647
// db1, key1 does not exist
37-
v, err := cache.getState("db1", "key1")
38-
require.NoError(t, err)
48+
v, inCache := cache.getState("db1", "key1")
49+
require.False(t, inCache)
3950
require.Nil(t, v)
4051

4152
s := &fastcache.Stats{}
@@ -54,11 +65,12 @@ func TestCache(t *testing.T) {
5465
}
5566
valBytes, err := proto.Marshal(db1Key1Value1)
5667
require.NoError(t, err)
57-
cache.putState("db1", "key1", valBytes)
68+
require.NoError(t, cache.putState("db1", "key1", valBytes))
5869

59-
// db1, key1 should exists
60-
actualKey1Value1, err := cache.getState("db1", "key1")
61-
require.NoError(t, err)
70+
// db1, key1 should exist
71+
actualKey1Value1, inCache := unmarshaler(t)(cache.getState("db1", "key1"))
72+
require.True(t, inCache)
73+
require.NotNil(t, actualKey1Value1)
6274
require.True(t, proto.Equal(db1Key1Value1, actualKey1Value1))
6375

6476
cache.dataCache.UpdateStats(s)
@@ -69,11 +81,12 @@ func TestCache(t *testing.T) {
6981
// update key1's value
7082
valBytes, err := proto.Marshal(db1Key1Value2)
7183
require.NoError(t, err)
72-
cache.putState("db1", "key1", valBytes)
84+
require.NoError(t, cache.putState("db1", "key1", valBytes))
7385

7486
// db1, key1 should have the updated value
75-
actualKey1Value2, err := cache.getState("db1", "key1")
76-
require.NoError(t, err)
87+
actualKey1Value2, inCache := unmarshaler(t)(cache.getState("db1", "key1"))
88+
require.True(t, inCache)
89+
require.NotNil(t, actualKey1Value2)
7790
require.True(t, proto.Equal(db1Key1Value2, actualKey1Value2))
7891

7992
s := &fastcache.Stats{}
@@ -87,8 +100,8 @@ func TestCache(t *testing.T) {
87100
require.NoError(t, err)
88101
cache.putStateIfExist("db2", "key1", valBytes)
89102

90-
v, err := cache.getState("db2", "key1")
91-
require.NoError(t, err)
103+
v, inCache := unmarshaler(t)(cache.getState("db2", "key1"))
104+
require.False(t, inCache)
92105
require.Nil(t, v)
93106

94107
s := &fastcache.Stats{}
@@ -99,15 +112,17 @@ func TestCache(t *testing.T) {
99112
t.Run("store same key in two different databases", func(t *testing.T) {
100113
valBytes, err := proto.Marshal(db2Key1Value1)
101114
require.NoError(t, err)
102-
cache.putState("db2", "key1", valBytes)
115+
require.NoError(t, cache.putState("db2", "key1", valBytes))
103116

104117
// both db1, key1 and db2, key1 should exist
105-
actualDB1Key1Value2, err := cache.getState("db1", "key1")
106-
require.NoError(t, err)
118+
actualDB1Key1Value2, inCache := unmarshaler(t)(cache.getState("db1", "key1"))
119+
require.True(t, inCache)
120+
require.NotNil(t, actualDB1Key1Value2)
107121
require.True(t, proto.Equal(db1Key1Value2, actualDB1Key1Value2))
108122

109-
actualDB2Key1Value1, err := cache.getState("db2", "key1")
110-
require.NoError(t, err)
123+
actualDB2Key1Value1, inCache := unmarshaler(t)(cache.getState("db2", "key1"))
124+
require.True(t, inCache)
125+
require.NotNil(t, actualDB2Key1Value1)
111126
require.True(t, proto.Equal(db2Key1Value1, actualDB2Key1Value1))
112127

113128
s := &fastcache.Stats{}

0 commit comments

Comments
 (0)