-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
WIP: [x/auth] Add lru cache for account #2678
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #2678 +/- ##
===========================================
+ Coverage 52.17% 58.83% +6.65%
===========================================
Files 136 152 +16
Lines 9619 9481 -138
===========================================
+ Hits 5019 5578 +559
+ Misses 4263 3532 -731
- Partials 337 371 +34 |
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.
Thanks @yutianwu! I left some remarks.
Gopkg.toml
Outdated
@@ -88,6 +88,10 @@ | |||
name = "github.com/mitchellh/go-homedir" | |||
version = "1.0.0" | |||
|
|||
[[constraint]] |
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.
All for using this lib, although we should remove our own LRUs now.
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'm not sure I agree, its kind of a simple thing to introduce a new dependency for. We already have a self-created LRU in tendermint.
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.
Where is our generalized LRU cache? This is the most commonly used LRU lib I've seen.
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.
@ValarDragon is probably referring to https://github.com/tendermint/tendermint/blob/c086d0a34102bd42873d20445673ea1d18a539cd/mempool/mempool.go#L595, but it's not generalized (not to the extent of hashicorp's LRU).
@@ -121,6 +123,35 @@ func (acc *BaseAccount) SetSequence(seq int64) error { | |||
return nil | |||
} | |||
|
|||
// Implements sdk.Account. |
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.
// Implements sdk.Account. | |
// Clone creates a deep copy of a BaseAccount. It implements the sdk.Account interface. |
// it should be fine if not deep copy them. if both of | ||
// the two interfaces can provide a Clone() method would be terrific. | ||
clonedAcc := &BaseAccount{ | ||
PubKey: acc.PubKey, |
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 don't need to set PubKey
or Address
here. They can be set after we copy their bytes.
x/auth/account.go
Outdated
Sequence: acc.Sequence, | ||
} | ||
|
||
addr := make([]byte, len(acc.Address)) |
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 would do the same thing for PubKey
. See comment above.
@@ -121,6 +123,35 @@ func (acc *BaseAccount) SetSequence(seq int64) error { | |||
return nil | |||
} | |||
|
|||
// Implements sdk.Account. | |||
func (acc *BaseAccount) Clone() Account { | |||
// given the fact PubKey and Address doesn't change, |
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'd remove this comment.
x/auth/mapper.go
Outdated
maxCacheNumber = 10000 | ||
) | ||
|
||
// accountLRUCache is a cache for the amino decoding of accounts. because we get and set accounts |
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.
// accountLRUCache is a cache for the amino decoding of accounts. because we get and set accounts | |
// accountLRUCache is a cache for the amino decoding of accounts. Because we get and set accounts |
x/auth/mapper.go
Outdated
func newAccountLRUCache(cap int) *accountLRUCache { | ||
cache, err := lru.New(cap) | ||
if err != nil { | ||
panic(err) |
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.
Can we error wrap this? i.e. errors.Wrap(err, "failed to create account LRU cache")
.
x/auth/mapper.go
Outdated
return nil, false | ||
} | ||
|
||
func (cache *accountLRUCache) addAccount(marshalled string, acc Account) { |
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 (cache *accountLRUCache) addAccount(marshalled string, acc Account) { | |
func (cache *accountLRUCache) setAccount(marshalled string, acc Account) { |
x/auth/mapper.go
Outdated
cache.cache.Add(marshalled, acc.Clone()) | ||
} | ||
|
||
// validatorCache-key: validator amino bytes |
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.
typo and should probably be at the top of the file.
thx for your comments, I am kind of busy this week. But I have a concern that if account data is too large, we may get a big key. |
Mhmm, good point. Hashing the key would also add overhead we don't want. |
Ideally, the account cache should be like this. we cache account struct in memory (CheckAccountCache, DeliverAccountCache), they share the common cache AccountCache which also caches Account struct in memory. CheckAccountCache and DeliverAccountCache should work like CacheMultiStore and we can commit the changes to AccountCache. And AccountCache uses a LRUCache for Accounts. All the caches use address as key, so we do not have to worry about the large key. Actually, it is kind of like simplified cache for Database and we do not need to care about replication delay between master and slave. @alexanderbez what's your view? |
/cc @jaekwon |
How much time is this saving? When the IAVL store gets more full (e.g. 100s of millions of items), what is the relative improvement then?
Some form of this is possible, but because CacheMultiStore etc already does most of this, instead it might make more sense to just find ways to make the encoding faster, which we will work on next year. Alternatively, I think it might make sense to consider an extension to the storage system, where we have KVStore but also a ClonerStore or KVClonerStore which wraps a KVStore... and it would be set in the context somehow... Let me think more about this, it requires more thinking but I don't have much time for it atm. |
Otherwise, I don't think we should merge this in yet without more information about how much time is actually being saved, given various store sizes. |
Lets start designing an KeyObjectStore wrapper around the KVStore. This should help. How should we keep in touch? What project are you with and are you in our slack? |
a09286f
to
3f9cda1
Compare
@jaekwon sorry, recently I have been busy testing the performance and just get the result, I have submit the prototype of account cache which I used in our project and it doubled our throughput. and the prototype implement the thought I described in the upper image. we can discuss the better implementation of the cache, but it surely help a lot. thx for you reply. actually I am not in you slack, can you kindly invite me? my email is wzxingbupt#gmail#com |
This is still a good idea but unfortunately quite out of date - @yutianwu are you planning to take this up again or should we close it? |
I'll close it ~ |
Ref: #2652
Add a lru cache for Account because getting and setting accounts happened frequently(CheckTx and DeliverTx) and decoding is time-consuming.