-
Notifications
You must be signed in to change notification settings - Fork 23
Cache block results #509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cache block results #509
Changes from 10 commits
7c8d0e1
30728a4
6d65d6b
93f69bf
498274b
c201a97
77c182a
9ec89c4
e82423b
cf1a7a8
9fc0379
acb543e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package mock | ||
|
|
||
| // TimedCacheMock - | ||
| type TimedCacheMock struct { | ||
| Cache map[string]interface{} | ||
| } | ||
|
|
||
| // NewTimedCacheMock - | ||
| func NewTimedCacheMock() *TimedCacheMock { | ||
| return &TimedCacheMock{Cache: make(map[string]interface{})} | ||
| } | ||
|
|
||
| // Put - | ||
| func (mock *TimedCacheMock) Put(key []byte, value interface{}) error { | ||
| mock.Cache[string(key)] = value | ||
| return nil | ||
| } | ||
|
|
||
| // Get - | ||
| func (mock *TimedCacheMock) Get(key []byte) (value interface{}, ok bool) { | ||
| val, found := mock.Cache[string(key)] | ||
| return val, found | ||
| } | ||
|
|
||
| // Close - | ||
| func (mock *TimedCacheMock) Close() error { | ||
| return nil | ||
| } | ||
|
|
||
| // IsInterfaceNil - | ||
| func (mock *TimedCacheMock) IsInterfaceNil() bool { | ||
| return mock == nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package mock | ||
|
|
||
| // TimedCacheStub - | ||
| type TimedCacheStub struct { | ||
| } | ||
|
|
||
| // Put - | ||
| func (stub *TimedCacheStub) Put(_ []byte, _ interface{}) error { | ||
| return nil | ||
| } | ||
|
|
||
| // Get - | ||
| func (stub *TimedCacheStub) Get(_ []byte) (value interface{}, ok bool) { | ||
| return nil, false | ||
| } | ||
|
|
||
| // Close - | ||
| func (stub *TimedCacheStub) Close() error { | ||
| return nil | ||
| } | ||
|
|
||
| // IsInterfaceNil - | ||
| func (stub *TimedCacheStub) IsInterfaceNil() bool { | ||
| return stub == nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,24 +35,39 @@ const ( | |
| rawPathStr = "raw" | ||
| ) | ||
|
|
||
| const ( | ||
| blockScope = "block" | ||
| hyperBlockScope = "hyperblock" | ||
| ) | ||
|
|
||
| // BlockProcessor handles blocks retrieving | ||
| type BlockProcessor struct { | ||
| proc Processor | ||
| proc Processor | ||
| cache TimedCache | ||
| } | ||
|
|
||
| // NewBlockProcessor will create a new block processor | ||
| func NewBlockProcessor(proc Processor) (*BlockProcessor, error) { | ||
| func NewBlockProcessor(proc Processor, cache TimedCache) (*BlockProcessor, error) { | ||
| if check.IfNil(proc) { | ||
| return nil, ErrNilCoreProcessor | ||
| } | ||
| if check.IfNil(cache) { | ||
| return nil, ErrNilTimedCache | ||
| } | ||
|
|
||
| return &BlockProcessor{ | ||
| proc: proc, | ||
| proc: proc, | ||
| cache: cache, | ||
| }, nil | ||
| } | ||
|
|
||
| // GetBlockByHash will return the block based on its hash | ||
| func (bp *BlockProcessor) GetBlockByHash(shardID uint32, hash string, options common.BlockQueryOptions) (*data.BlockApiResponse, error) { | ||
| scope := fmt.Sprintf("%s:shardID=%d", blockScope, shardID) | ||
AdoAdoAdo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if cached := getObjectFromCacheWithHash[*data.BlockApiResponse](bp.cache, scope, hash, options); cached != nil { | ||
| return cached, nil | ||
| } | ||
|
|
||
| observers, err := bp.getObserversOrFullHistoryNodes(shardID) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -62,23 +77,28 @@ func (bp *BlockProcessor) GetBlockByHash(shardID uint32, hash string, options co | |
|
|
||
| response := data.BlockApiResponse{} | ||
| for _, observer := range observers { | ||
|
|
||
| _, err := bp.proc.CallGetRestEndPoint(observer.Address, path, &response) | ||
| if err != nil { | ||
| log.Error("block request", "observer", observer.Address, "error", err.Error()) | ||
| continue | ||
| } | ||
|
|
||
| log.Info("block request", "shard id", observer.ShardId, "hash", hash, "observer", observer.Address) | ||
| return &response, nil | ||
|
|
||
| bp.cacheObject(&response, scope, options) | ||
| return &response, nil | ||
|
Comment on lines
72
to
+89
|
||
| } | ||
|
|
||
| return nil, WrapObserversError(response.Error) | ||
| } | ||
|
|
||
| // GetBlockByNonce will return the block based on the nonce | ||
| func (bp *BlockProcessor) GetBlockByNonce(shardID uint32, nonce uint64, options common.BlockQueryOptions) (*data.BlockApiResponse, error) { | ||
| scope := fmt.Sprintf("%s:shardID=%d", blockScope, shardID) | ||
| if cached := getObjectFromCacheWithNonce[*data.BlockApiResponse](bp.cache, scope, nonce, options); cached != nil { | ||
| return cached, nil | ||
| } | ||
|
|
||
| observers, err := bp.getObserversOrFullHistoryNodes(shardID) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -88,16 +108,15 @@ func (bp *BlockProcessor) GetBlockByNonce(shardID uint32, nonce uint64, options | |
|
|
||
| response := data.BlockApiResponse{} | ||
| for _, observer := range observers { | ||
|
|
||
| _, err := bp.proc.CallGetRestEndPoint(observer.Address, path, &response) | ||
| if err != nil { | ||
| log.Error("block request", "observer", observer.Address, "error", err.Error()) | ||
| continue | ||
| } | ||
|
|
||
| log.Info("block request", "shard id", observer.ShardId, "nonce", nonce, "observer", observer.Address) | ||
| bp.cacheObject(&response, scope, options) | ||
| return &response, nil | ||
|
Comment on lines
117
to
119
|
||
|
|
||
| } | ||
|
|
||
| return nil, WrapObserversError(response.Error) | ||
|
|
@@ -114,6 +133,10 @@ func (bp *BlockProcessor) getObserversOrFullHistoryNodes(shardID uint32) ([]*dat | |
|
|
||
| // GetHyperBlockByHash returns the hyperblock by hash | ||
| func (bp *BlockProcessor) GetHyperBlockByHash(hash string, options common.HyperblockQueryOptions) (*data.HyperblockApiResponse, error) { | ||
| if cached := getObjectFromCacheWithHash[*data.HyperblockApiResponse](bp.cache, hyperBlockScope, hash, options); cached != nil { | ||
| return cached, nil | ||
| } | ||
|
|
||
| builder := &hyperblockBuilder{} | ||
|
|
||
| blockQueryOptions := common.BlockQueryOptions{ | ||
|
|
@@ -136,7 +159,10 @@ func (bp *BlockProcessor) GetHyperBlockByHash(hash string, options common.Hyperb | |
| } | ||
|
|
||
| hyperblock := builder.build(options.NotarizedAtSource) | ||
| return data.NewHyperblockApiResponse(hyperblock), nil | ||
| hyperBlockRsp := data.NewHyperblockApiResponse(hyperblock) | ||
| bp.cacheObject(hyperBlockRsp, hyperBlockScope, options) | ||
|
|
||
| return hyperBlockRsp, nil | ||
| } | ||
|
|
||
| func (bp *BlockProcessor) addShardBlocks( | ||
|
|
@@ -181,6 +207,10 @@ func (bp *BlockProcessor) getAlteredAccountsIfNeeded(options common.HyperblockQu | |
|
|
||
| // GetHyperBlockByNonce returns the hyperblock by nonce | ||
| func (bp *BlockProcessor) GetHyperBlockByNonce(nonce uint64, options common.HyperblockQueryOptions) (*data.HyperblockApiResponse, error) { | ||
| if cached := getObjectFromCacheWithNonce[*data.HyperblockApiResponse](bp.cache, hyperBlockScope, nonce, options); cached != nil { | ||
| return cached, nil | ||
| } | ||
|
|
||
| builder := &hyperblockBuilder{} | ||
|
|
||
| blockQueryOptions := common.BlockQueryOptions{ | ||
|
|
@@ -203,7 +233,10 @@ func (bp *BlockProcessor) GetHyperBlockByNonce(nonce uint64, options common.Hype | |
| } | ||
|
|
||
| hyperblock := builder.build(options.NotarizedAtSource) | ||
| return data.NewHyperblockApiResponse(hyperblock), nil | ||
| hyperBlockRsp := data.NewHyperblockApiResponse(hyperblock) | ||
| bp.cacheObject(hyperBlockRsp, hyperBlockScope, options) | ||
|
|
||
| return hyperBlockRsp, nil | ||
| } | ||
|
|
||
| // GetInternalBlockByHash will return the internal block based on its hash | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package process | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| ) | ||
|
|
||
| type cacheableBlock interface { | ||
| Hash() string | ||
| Nonce() uint64 | ||
| } | ||
|
|
||
| // No error checks for this cache. | ||
| // These caching errors should never happen, and if they do, they should not be blocking | ||
|
|
||
| func (bp *BlockProcessor) cacheObject(obj cacheableBlock, scope string, opts interface{}) { | ||
| objKey := makeObjKey(scope, obj.Hash(), opts) | ||
|
|
||
| // Store object | ||
| _ = bp.cache.Put(objKey, obj) | ||
|
|
||
| // Store nonce + hash lookup keys | ||
| _ = bp.cache.Put(makeHashCacheKey(scope, obj.Hash(), opts), objKey) | ||
| _ = bp.cache.Put(makeNonceCacheKey(scope, obj.Nonce(), opts), objKey) | ||
| } | ||
|
|
||
| func makeObjKey(scope string, hash string, opts interface{}) []byte { | ||
| optBytes, _ := json.Marshal(opts) | ||
| return []byte(scope + ":" + hash + "|" + string(optBytes)) | ||
AdoAdoAdo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| func makeHashCacheKey(scope string, hash string, opts interface{}) []byte { | ||
| optBytes, _ := json.Marshal(opts) | ||
| return []byte(fmt.Sprintf("%s:hash:%s|opts:%s", scope, hash, string(optBytes))) | ||
| } | ||
|
|
||
| func makeNonceCacheKey(scope string, nonce uint64, opts interface{}) []byte { | ||
| optBytes, _ := json.Marshal(opts) | ||
mariusmihaic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
AdoAdoAdo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return []byte(fmt.Sprintf("%s:nonce:%d|opts:%s", scope, nonce, string(optBytes))) | ||
| } | ||
|
|
||
| func getObjectFromCacheWithHash[T cacheableBlock](c TimedCache, scope string, hash string, opts interface{}) T { | ||
| return getObjFromCache[T](c, makeHashCacheKey(scope, hash, opts)) | ||
| } | ||
|
|
||
| func getObjectFromCacheWithNonce[T cacheableBlock](c TimedCache, scope string, nonce uint64, opts interface{}) T { | ||
| return getObjFromCache[T](c, makeNonceCacheKey(scope, nonce, opts)) | ||
| } | ||
|
|
||
| func getObjFromCache[T cacheableBlock](c TimedCache, lookUpKey []byte) T { | ||
| var retObj T | ||
|
|
||
| key, _ := c.Get(lookUpKey) | ||
| if key == nil { | ||
| return retObj | ||
| } | ||
|
|
||
| val, ok := c.Get(key.([]byte)) | ||
| if !ok { | ||
| return retObj | ||
| } | ||
|
|
||
| return val.(T) | ||
mariusmihaic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.