AstarNetwork / astar-frame

Core frame modules for Astar & Shiden network.
Other
58 stars 38 forks source link

Scalable staking info rotation #46

Closed Dinonard closed 1 year ago

Dinonard commented 2 years ago

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

  1. register and unregister shouldn't be usable until all values have been copied over
  2. introduction of migration logic makes the on_initialize function fairly complex (much more than I'd like it to be)
  3. 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:

fn on_initialize(n: T::BlockNumber) -> Weight {
    if T::ShouldEndSession::should_end_session(n) {
        Self::rotate_session();
        T::BlockWeights::get().max_block
    } else {
        0
    }
}

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

Check list

This pull request makes the following changes:

Adds

Fixes

Changes

To-dos

*Feel free to remove this section if it's not applicable

Dinonard commented 1 year ago

Will be handled differently in dApps staking v3.