-
Notifications
You must be signed in to change notification settings - Fork 43
[execution] Improve caching in accessors + simplify code #958
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
base: block-accessor
Are you sure you want to change the base?
Conversation
22aee9a to
216db6c
Compare
nil/internal/db/accessors.go
Outdated
| func GetFromShard(tx RoTx, shardId types.ShardId, table ShardedTableName, key prettyKey) ([]byte, error) { | ||
| data, err := tx.GetFromShard(shardId, table, key.Bytes()) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("%w: key=%s", err, key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps shardId we also want to add to the error message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to #969
nil/internal/db/badger.go
Outdated
| item, err := tx.tx.Get(MakeKey(tableName, key)) | ||
| if errors.Is(err, badger.ErrKeyNotFound) { | ||
| return nil, ErrKeyNotFound | ||
| return nil, fmt.Errorf("%w: table %s", ErrKeyNotFound, tableName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have quite a few checks for errors.Is(err, db.ErrKeyNotFound). I think we might break some logic with such changes. If we want to add context to ErrrKeyNotFound, perhaps we should make it a full type and change the errors.Is checks to errors.As. Also, it might be better to make such changes as a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed it here and added this to the Gets in accessors.
Wrapping errors this way doesn't affect errors.Is.
Moved these changes to another PR: #969
| outTxCountsLRU *lru.Cache[common.Hash, [][]byte] | ||
| receiptsLRU *lru.Cache[common.Hash, [][]byte] | ||
| } | ||
| func newRawAccessorCache(blocksLRUSize, txnRLUSize int) *cache { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| func newRawAccessorCache(blocksLRUSize, txnRLUSize int) *cache { | |
| func newRawAccessorCache(blocksLRUSize, txnLRUSize int) *cache { |
| b.withDbTimestamp = true | ||
| return b | ||
| } | ||
| res, err := db.ReadBlockBytes(s.tx, s.shardId, hash) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also check s.cache.blocksLRU
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworked it a bit.
| return nil, err | ||
| } | ||
| sa.cache.blocksLRU.Add(hash, block) | ||
| return &types.RawBlockWithExtractedData{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like this use of a type without filling in some of the fields. If Go were a normal language, we should use variant here, but since we have what we have, let's keep some form of rawBlockAccessorResult to represent the mandatory + optional part of the data fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I based it on the usages, but they actually could be improved/simplified. So, did that.
| check.PanicIfErr(err) | ||
| type cache struct { | ||
| blocksLRU *lru.Cache[common.Hash, *types.Block] | ||
| fullBlocksLRU *lru.Cache[common.Hash, *types.RawBlockWithExtractedData] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered a more normalized way of storing data, where the ExtractedData part is stored separately, and rawBlocksLRU is used for both raw headers and full blocks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then I'll have to introduce ExtractedData, etc. Having an extra link to a block header is not a problem.
Instead, I merged raw and decoded headers, since they are unlikely to be used one without the other (it can only happen with the raw ones and only in a single rpc method).
| withDbTimestamp bool | ||
| withConfig bool | ||
| } | ||
| func (s RawShardAccessor) decodeBlock(hash common.Hash, data []byte) (*types.Block, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason I really want to see in the function name the information that it is caching.
| func (s RawShardAccessor) decodeBlock(hash common.Hash, data []byte) (*types.Block, error) { | |
| func (s RawShardAccessor) decodeBlockCached(hash common.Hash, data []byte) (*types.Block, error) { |
Probably because otherwise it's very easy to think from the name that it's pure, and make incorrect assumptions about behavior by reading the call locations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, but removed this method altogether.
| if len(errMsg) > 0 { | ||
| result.Errors[txnHash] = errMsg | ||
| if len(errMsg) > 0 { | ||
| if result.Errors == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about building a temporary dictionary, and assigning it to result.Errors at the end if it's not empty? Or conversely, assigning nil to result.Errors if it's empty. By the way, why do we even want to maintain such an invariant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it would be nice not to touch the result, unless there are actual errors.
| s.checkReceipt(shardId, m) | ||
| }) | ||
|
|
||
| s.Run("CheckRefundsSeqno", func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did we delete this test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't have anything to do with the proposer functionality.
216db6c to
f41374e
Compare
f41374e to
2bfd100
Compare
2bfd100 to
ba582e6
Compare
ba582e6 to
bc76ef8
Compare
bc76ef8 to
0ffafe2
Compare
0ffafe2 to
200bbca
Compare
200bbca to
528bc74
Compare
528bc74 to
0f8e20c
Compare
0f8e20c to
d4b066d
Compare
d4b066d to
bf0abe8
Compare
bf0abe8 to
dffdbbb
Compare
| blockHashByNumber *BlockHashByNumberAccessor | ||
| } | ||
|
|
||
| func NewStateAccessor(blockLRUSize, txnLRUSize int) *StateAccessor { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw if you wanna improve cache it may make sense to implement slightly different algo than simple LRU
https://en.wikipedia.org/wiki/Adaptive_replacement_cache - this one seems to be quite efficient
https://github.com/alexanderGugel/arc - and here's some implementation in go
State accessor appears in profiles as an active creator of objects in memory.
Here I simplify the code of the accessor tailoring it to the current needs. The caching is reworked and improved (it wasn't happening at some places at all).
There are also some simplifications in the other code, especially improvements in the usage of accessors.