After discovering the issue with using iterators when finding the correct ContractEraStake value (each iterator key read is considered as one DB read), we had to switch our approach to copying old ContractEraStake values from ending era to the
beginning era.
So each time era X ends and era X+1 is about to begin, for each active contract we need to copy the ContractEraStake(contract, X) to ContractEraStake(contract, X+1).
This is done on initialize and the problem is that it scales with number of dApps.
Solution
Instead of copying all values at once, break it into multiple steps.
E.g. in first block of an era, we copy N values. In second block, we copy another N values, and so on.
We also utilize the on_idle hook to maximize usage of block time.
However, this brings various problems.
register and unregister shouldn't be usable until all values have been copied over
introduction of migration logic makes the on_initialize function fairly complex (much more than I'd like it to be)
on_initialize has many logical paths, of which some have linear complexity so it's difficult to benchmark automatically, and instead is done manually
Alternative
Keep the current solution as it is.
E.g. pallet_session's on_initialize will consume the entire block:
Therefore, we could keep on using the same approach and just return the consumed weight.
Since one copy-over operation requires 2 DB reads and 1 DB write, using RocksDb params, we get the total weight of one copy:
2 * 25_000_000 + 1 * 100_000_000 = 150_000_000.
The max block weight is configured to be 0.5 [s]. Taking into account only operational part of the block into account, this gives us total of 375_000_000_000 weight. This is actually larger since we have part for the on_initialize execution.
This means we have enough capacity to copy over 2500ContractEraStake entries.
This would also mean that we have 2500 active dApps in dApps staking.
Also worth noting is that once we switch from RocksDb to ParityDb, we'll have enough capacity for almost ~5800 copy operations.
TODO
[ ] discuss solution with the team
[x] check that on_initialize weights are correct
[x] consider benchmarking the rotation function
[x] add unit tests
[ ] look into whitelisting DB entries
[ ] benchmark register/unregister
Check list
[ ] contains breaking changes
[ ] adds new feature
[ ] modifies existing feature (bug fix or improvements)
[ ] relies on other tasks
[ ] documentation changes
[ ] tests and/or benchmarks are included
[ ] changed API client type definition or chain metadata
This pull request makes the following changes:
Adds
(ex: Add feature A)
Fixes
(ex: Fix validation function)
Changes
(ex: Change document B)
To-dos
*Feel free to remove this section if it's not applicable
Pull Request Summary
Scalabe Staking Info Rotation
Problem
After discovering the issue with using iterators when finding the correct
ContractEraStake
value (each iterator key read is considered as one DB read), we had to switch our approach to copying oldContractEraStake
values from ending era to the beginning era.So each time era X ends and era X+1 is about to begin, for each active contract we need to copy the
ContractEraStake(contract, X)
toContractEraStake(contract, X+1)
.This is done on initialize and the problem is that it scales with number of dApps.
Solution
Instead of copying all values at once, break it into multiple steps. E.g. in first block of an era, we copy N values. In second block, we copy another N values, and so on. We also utilize the
on_idle
hook to maximize usage of block time.However, this brings various problems.
register
andunregister
shouldn't be usable until all values have been copied overon_initialize
function fairly complex (much more than I'd like it to be)on_initialize
has many logical paths, of which some have linear complexity so it's difficult to benchmark automatically, and instead is done manuallyAlternative
Keep the current solution as it is. E.g.
pallet_session
'son_initialize
will consume the entire block:Therefore, we could keep on using the same approach and just return the consumed weight.
Since one copy-over operation requires 2 DB reads and 1 DB write, using RocksDb params, we get the total weight of one copy:
2 * 25_000_000 + 1 * 100_000_000 = 150_000_000
.The max block weight is configured to be
0.5 [s]
. Taking into account only operational part of the block into account, this gives us total of375_000_000_000
weight. This is actually larger since we have part for theon_initialize
execution.This means we have enough capacity to copy over 2500
ContractEraStake
entries. This would also mean that we have 2500 active dApps in dApps staking.Also worth noting is that once we switch from RocksDb to ParityDb, we'll have enough capacity for almost ~5800 copy operations.
TODO
on_initialize
weights are correctCheck list
This pull request makes the following changes:
Adds
Fixes
Changes
To-dos