-
Notifications
You must be signed in to change notification settings - Fork 324
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[api] add cache for ReadContract/State() (#2827)
- Loading branch information
Showing
4 changed files
with
196 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package api | ||
|
||
import ( | ||
"encoding/json" | ||
"sync" | ||
|
||
"github.com/iotexproject/go-pkgs/hash" | ||
"go.uber.org/zap" | ||
|
||
"github.com/iotexproject/iotex-core/blockchain/block" | ||
"github.com/iotexproject/iotex-core/pkg/log" | ||
) | ||
|
||
type ( | ||
// ReadKey represents a read key | ||
ReadKey struct { | ||
Name string `json:"name,omitempty"` | ||
Height string `json:"height,omitempty"` | ||
Method []byte `json:"method,omitempty"` | ||
Args [][]byte `json:"args,omitempty"` | ||
} | ||
|
||
// ReadCache stores read results | ||
ReadCache struct { | ||
total, hit int | ||
lock sync.RWMutex | ||
bins map[hash.Hash160][]byte | ||
} | ||
) | ||
|
||
// Hash returns the hash of key's json string | ||
func (k *ReadKey) Hash() hash.Hash160 { | ||
b, _ := json.Marshal(k) | ||
return hash.Hash160b(b) | ||
} | ||
|
||
// NewReadCache returns a new read cache | ||
func NewReadCache() *ReadCache { | ||
return &ReadCache{ | ||
bins: make(map[hash.Hash160][]byte), | ||
} | ||
} | ||
|
||
// Get reads according to key | ||
func (rc *ReadCache) Get(key hash.Hash160) ([]byte, bool) { | ||
rc.lock.RLock() | ||
defer rc.lock.RUnlock() | ||
|
||
rc.total++ | ||
d, ok := rc.bins[key] | ||
if !ok { | ||
return nil, false | ||
} | ||
rc.hit++ | ||
if rc.hit%100 == 0 { | ||
log.L().Info("API cache hit", zap.Int("total", rc.total), zap.Int("hit", rc.hit)) | ||
} | ||
return d, true | ||
} | ||
|
||
// Put writes according to key | ||
func (rc *ReadCache) Put(key hash.Hash160, value []byte) { | ||
rc.lock.Lock() | ||
rc.bins[key] = value | ||
rc.lock.Unlock() | ||
} | ||
|
||
// Clear clears the cache | ||
func (rc *ReadCache) Clear() { | ||
rc.lock.Lock() | ||
rc.bins = nil | ||
rc.bins = make(map[hash.Hash160][]byte) | ||
rc.lock.Unlock() | ||
} | ||
|
||
// Respond implements the Responder interface | ||
func (rc *ReadCache) Respond(*block.Block) error { | ||
// invalidate the cache at every new block | ||
rc.Clear() | ||
return nil | ||
} | ||
|
||
// Exit implements the Responder interface | ||
func (rc *ReadCache) Exit() { | ||
rc.Clear() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package api | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/iotexproject/go-pkgs/hash" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestReadKey(t *testing.T) { | ||
r := require.New(t) | ||
|
||
var keys []hash.Hash160 | ||
for _, v := range []ReadKey{ | ||
{"staking", "10", []byte("activeBuckets"), [][]byte{[]byte{0, 1}, []byte{2, 3, 4, 5, 6, 7, 8}}}, | ||
{"staking", "10", []byte("activeBuckets"), [][]byte{[]byte{0, 1, 2}, []byte{3, 4, 5, 6, 7, 8}}}, | ||
{"staking", "10", []byte("activeBuckets"), [][]byte{[]byte{0, 1, 2, 3}, []byte{4, 5, 6, 7, 8}}}, | ||
{"staking", "10", []byte("activeBuckets"), [][]byte{[]byte{0, 1, 2, 3, 4, 5}, []byte{6, 7, 8}}}, | ||
{"staking", "10", []byte("activeBuckets"), [][]byte{[]byte{0, 1, 2, 3, 4, 5, 6, 7}, []byte{8}}}, | ||
} { | ||
keys = append(keys, v.Hash()) | ||
} | ||
|
||
// all keys are different | ||
for i := range keys { | ||
k := keys[i] | ||
for j := i + 1; j < len(keys); j++ { | ||
r.NotEqual(k, keys[j]) | ||
} | ||
} | ||
} | ||
|
||
func TestReadCache(t *testing.T) { | ||
r := require.New(t) | ||
|
||
c := NewReadCache() | ||
rcTests := []struct { | ||
k hash.Hash160 | ||
v []byte | ||
}{ | ||
{hash.Hash160b([]byte{1}), []byte{1}}, | ||
{hash.Hash160b([]byte{2}), []byte{2}}, | ||
{hash.Hash160b([]byte{3}), []byte{1}}, | ||
{hash.Hash160b([]byte{4}), []byte{2}}, | ||
} | ||
for _, v := range rcTests { | ||
d, ok := c.Get(v.k) | ||
r.False(ok) | ||
r.Nil(d) | ||
c.Put(v.k, v.v) | ||
} | ||
|
||
for _, v := range rcTests { | ||
d, ok := c.Get(v.k) | ||
r.True(ok) | ||
r.Equal(v.v, d) | ||
} | ||
|
||
c.Clear() | ||
for _, v := range rcTests { | ||
d, ok := c.Get(v.k) | ||
r.False(ok) | ||
r.Nil(d) | ||
} | ||
} |