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

rpc: Fix response time grow over time #3438

Closed
wants to merge 1 commit into from

Conversation

unclezoro
Copy link
Contributor

/validators rpc response time grow with time.

Steps To Reproduce:

  1. start a new node from hight 1
    2.time curl 127.0.0.1:26657/validators, it takes 0.005s
  2. let the chain run for serveral days, and change no validators.in my scenario the height is : 1584840
  3. time curl 127.0.0.1:26857/validators, it takes 1.25s

The api consumes cpu resouces, in the long run, a small scale of DDOS can make the node
a zombie process.

Acturally the logic to do IncrementProposerPriority by height - valInfo.LastHeightChanged times will not replay the exactly ProposerPriority once there are two or more round.

  • Updated all relevant documentation in docs
  • Updated all code comments where relevant
  • Wrote tests
  • Updated CHANGELOG_PENDING.md

@unclezoro unclezoro changed the base branch from master to develop March 17, 2019 13:26
valInfo.LastHeightChanged,
height,
),
)
}
valInfo2.ValidatorSet.IncrementProposerPriority(int(height - valInfo.LastHeightChanged)) // mutate
valInfo = valInfo2
Copy link
Contributor

Choose a reason for hiding this comment

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

A much simpler solution here might be saving/memoizing valInfo2 saveValidatorsInfo(db, height, valInfo2) instead of the whole shebang with the blockstore. Thoughts?

Copy link
Contributor Author

@unclezoro unclezoro Mar 18, 2019

Choose a reason for hiding this comment

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

A much simpler solution here might be saving/memoizing valInfo2 saveValidatorsInfo(db, height, valInfo2) instead of the whole shebang with the blockstore. Thoughts?

Ofcourse, if we can save all validatorInfo. The previous implement only save whole validatorInfo of latest height.

Copy link
Contributor Author

@unclezoro unclezoro Mar 18, 2019

Choose a reason for hiding this comment

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

A much simpler solution here might be saving/memoizing valInfo2 saveValidatorsInfo(db, height, valInfo2) instead of the whole shebang with the blockstore. Thoughts?

so should I modify code to saveValidatorsInfo(db, height, valInfo2), since it store redundant information ? @melekes updated. you may have a review.

@codecov-io
Copy link

codecov-io commented Mar 18, 2019

Codecov Report

Merging #3438 into develop will increase coverage by 0.01%.
The diff coverage is 57.14%.

@@             Coverage Diff             @@
##           develop    #3438      +/-   ##
===========================================
+ Coverage     64.2%   64.21%   +0.01%     
===========================================
  Files          213      213              
  Lines        17362    17325      -37     
===========================================
- Hits         11147    11126      -21     
+ Misses        5290     5278      -12     
+ Partials       925      921       -4
Impacted Files Coverage Δ
state/store.go 68.53% <57.14%> (-0.76%) ⬇️
libs/db/remotedb/remotedb.go 35.89% <0%> (-4.94%) ⬇️
libs/events/events.go 93.2% <0%> (-4.86%) ⬇️
blockchain/pool.go 80.26% <0%> (-1.98%) ⬇️
blockchain/reactor.go 71.49% <0%> (-0.97%) ⬇️
rpc/client/httpclient.go 65.62% <0%> (-0.9%) ⬇️
consensus/reactor.go 71.54% <0%> (-0.12%) ⬇️
libs/db/remotedb/grpcdb/client.go 0% <0%> (ø) ⬆️
p2p/pex/addrbook.go 67.89% <0%> (+0.49%) ⬆️
p2p/pex/pex_reactor.go 79.87% <0%> (+0.62%) ⬆️
... and 2 more

state/store.go Outdated
)
}
valInfo2.ValidatorSet.IncrementProposerPriority(int(height - valInfo.LastHeightChanged)) // mutate
valInfo = valInfo2
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we'd save valInfo2 here, not save for every height! I.e. we only call save once somebody tries to access it.

Copy link
Contributor Author

@unclezoro unclezoro Mar 18, 2019

Choose a reason for hiding this comment

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

I thought we'd save valInfo2 here, not save for every height! I.e. we only call save once somebody tries to access it.

But the replay result is not correct when there are two or more rounds at one height.

Copy link
Contributor

Choose a reason for hiding this comment

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

How so? If we're using int(height - valInfo.LastHeightChanged) difference

Copy link
Contributor

@liamsi liamsi Mar 19, 2019

Choose a reason for hiding this comment

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

But the replay result is not correct when there are two or more rounds at one height.

Is that because we call IncrementProposerPriority per round for the same height (several times) then? Can you give an example (ideally a test) which clearly shows what you mean by "not correct" here @guagualvcha? That would be awesome.

Copy link
Contributor

Choose a reason for hiding this comment

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

Note that the per-height proposer selection is independent of the per-round proposer selection. So the proposer at height=1 round=1 is the same as the proposer for height=2 round=0. Ie. see #3044

@ebuchman
Copy link
Contributor

Maybe we need a second implementation of the IncrementProposerPriority for when we know the validator set hasn't changed that can be computed much more quickly for this use case. cc @ancazamfir

@unclezoro
Copy link
Contributor Author

Maybe we need a second implementation of the IncrementProposerPriority for when we know the validator set hasn't changed that can be computed much more quickly for this use case. cc @ancazamfir

Great, that would be the final solution.

@xla xla changed the title fix rpc response time grow with time. rpc: Fix response time grow over time Mar 20, 2019
@unclezoro unclezoro mentioned this pull request Mar 22, 2019
@ancazamfir
Copy link
Contributor

h1 - the height of previous validator set change
h - the height of the status query
h2 - the current height

if h == h2 we could get the set from the state; else we have to do IncrementProposerPriority(h-h1) and I can't think of a much better way to compute it faster and still be accurate wrt proposer priorities.

Maybe something like this:

  • call:
    IncrementProposerPriority(int(height - valInfo.LastHeightChanged)%valInfo.TotalVotingPower())
  • memoize the valInfo every time we call RescalePriorities(). This happens even without validator set changes although rarely.

May still be costly for high total voting power values. Alternatively we could save the valInfo every N blocks and then call Increment with max N times.

I thought we'd save valInfo2 here, not save for every height! I.e. we only call save once somebody tries to access it.

@melekes - could you please clarify? There may be a lot of accesses for different heights, isn't memory usage a concern?

@unclezoro
Copy link
Contributor Author

h1 - the height of previous validator set change
h - the height of the status query
h2 - the current height

if h == h2 we could get the set from the state; else we have to do IncrementProposerPriority(h-h1) and I can't think of a much better way to compute it faster and still be accurate wrt proposer priorities.

Maybe something like this:

  • call:
    IncrementProposerPriority(int(height - valInfo.LastHeightChanged)%valInfo.TotalVotingPower())
  • memoize the valInfo every time we call RescalePriorities(). This happens even without validator set changes although rarely.

May still be costly for high total voting power values. Alternatively we could save the valInfo every N blocks and then call Increment with max N times.

I thought we'd save valInfo2 here, not save for every height! I.e. we only call save once somebody tries to access it.

@melekes - could you please clarify? There may be a lot of accesses for different heights, isn't memory usage a concern?

@melekes @ancazamfir updated as ancazamfir said. Please have a review.

@melekes
Copy link
Contributor

melekes commented Apr 9, 2019

could you please clarify? There may be a lot of accesses for different heights, isn't memory usage a concern?

yes, but not big though comparing to DoS attack vector.

@melekes
Copy link
Contributor

melekes commented Apr 9, 2019

@guagualvcha please refrain from doing a rebase & force-push next time. When/if you do git rebase and force-push, 1) we can't see commit history 2) we can't review only new changes and forced to review everything every time. Thank you.

@ancazamfir
Copy link
Contributor

@guagualvcha Have you tested this? What is the response time now? What about with 1<<14 or 15?

@melekes
Copy link
Contributor

melekes commented Apr 9, 2019

Closing in favor of #3537

@melekes melekes closed this Apr 9, 2019
@@ -9,6 +9,10 @@ import (
"github.com/tendermint/tendermint/types"
)

const (
ValidatorSetStoreInterval = 1 << 10
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need to expose it

Copy link
Contributor

Choose a reason for hiding this comment

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

let's rename to valSetStoreInterval and add a comment persist validator set every valSetStoreInterval blocks to avoid LoadValidators taking too much time. Refs https://github.com/tendermint/tendermint/pull/3438

if valInfo.ValidatorSet == nil {
valInfo2 := loadValidatorsInfo(db, valInfo.LastHeightChanged)
queryHeight := valInfo.LastHeightChanged
Copy link
Contributor

Choose a reason for hiding this comment

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

let's create a var called lastStoreHeight := height - height%valSetStoreInterval

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.

None yet

6 participants