Skip to content
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

Closed
wants to merge 1 commit into from

Conversation

yutianwu
Copy link
Contributor

@yutianwu yutianwu commented Nov 4, 2018

Ref: #2652

Add a lru cache for Account because getting and setting accounts happened frequently(CheckTx and DeliverTx) and decoding is time-consuming.

@yutianwu yutianwu requested a review from jaekwon as a code owner November 4, 2018 10:53
@codecov
Copy link

codecov bot commented Nov 4, 2018

Codecov Report

Merging #2678 into develop will increase coverage by 6.65%.
The diff coverage is 91.66%.

@@             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

@yutianwu yutianwu changed the title [R4R] x/auth: add lru cache for account R4R: [x/auth] Add lru cache for account Nov 4, 2018
Copy link
Contributor

@alexanderbez alexanderbez left a 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]]
Copy link
Contributor

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.

Copy link
Contributor

@ValarDragon ValarDragon Nov 4, 2018

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.

Copy link
Contributor

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.

Copy link
Contributor

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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,
Copy link
Contributor

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.

Sequence: acc.Sequence,
}

addr := make([]byte, len(acc.Address))
Copy link
Contributor

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,
Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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)
Copy link
Contributor

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
Copy link
Contributor

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.

x/auth/mapper.go Outdated Show resolved Hide resolved
@yutianwu
Copy link
Contributor Author

yutianwu commented Nov 6, 2018

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.

@alexanderbez
Copy link
Contributor

Mhmm, good point. Hashing the key would also add overhead we don't want.

@yutianwu
Copy link
Contributor Author

yutianwu commented Nov 6, 2018

image

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.
When we commit a block, we will write the changes to AccountCache which will marshal account struct and save to disk.

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?

@yutianwu yutianwu changed the title R4R: [x/auth] Add lru cache for account WIP: [x/auth] Add lru cache for account Nov 12, 2018
@alexanderbez
Copy link
Contributor

/cc @jaekwon

@jaekwon
Copy link
Contributor

jaekwon commented Dec 6, 2018

@yutianwu

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?

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.

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.

@jaekwon
Copy link
Contributor

jaekwon commented Dec 6, 2018

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.

@jaekwon
Copy link
Contributor

jaekwon commented Dec 9, 2018

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?

@yutianwu
Copy link
Contributor Author

yutianwu commented Dec 11, 2018

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?

@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

@cwgoes
Copy link
Contributor

cwgoes commented Jan 17, 2019

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?

@yutianwu
Copy link
Contributor Author

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 ~

@yutianwu yutianwu closed this Jan 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants