Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

rpc/chainHead: Fix pruned blocks events from forks #13379

Merged
merged 39 commits into from
Mar 7, 2023

Conversation

lexnv
Copy link
Contributor

@lexnv lexnv commented Feb 13, 2023

This PR fixes the chainHead::follow edge cases:

  1. The Finalized event reported pruned blocks from soon-to-be-pruned forks without issuing a NewBlock first
  2. Finalized event reported as pruned just the stale heads, not the entire fork

Issue 1

The chainHead::follow method returns the Finalized event containing both finalized hashes and pruned block hashes. The substrate database will perform the pruning of height N at the finalization N + 1.

Considering the following tree structure:

A - A1 - A2 - A3
  - A1 - B2

Initial Condition:

  • User subscribes after A2 is finalized, but before A3 is finalized
  • Spec V2 dictates that in-memory blocks must first be reported.
    • first event: Initialized event containing the finalized hash (A2)
    • multiple events: NewBlock events for children of finalized (A3)

The NewBlock event contains the hash of the block + the parent hash. Taken from the spec:

parentBlockHash is guaranteed to be equal either to the current finalized block hash, or to a block reported in an earlier newBlock event.

Issues:

  • When A2 is finalized, we know that the fork [A1 - B2] is stale; however pruning happens for height N - 1
  • When A3 is finalized, we prune B2 (height 3 prunes stale forks on height 2)
  • B2 is reported as pruned; however it was never reported by the initial events

Solution:

  • When generating the initial events for in-memory blocks keep track of stale forks
  • Ignore stale forks when reporting the finalized event

Issue 2

chainHead::follow method reported just the stale heads when declaring the pruned hashes.

prunedBlockHashes contains, in no particular order, a list of blocks that are not descendants of the latest finalized block. These blocks will never be finalized and can be discarded.

To fix this issue, the tree route is determined from the stale heads to the canonical chain and all blocks are reported.

Additional

Because the complexity of generating the follow events grew, the logic is moved to the chain_head_follow.rs file.
This also helps with code readability and maintenance.

Under intensive testing, it was possible for the Finalized event to be received before the NewBlock that led to flaky scenarios. To close the gap, the Finalized branch can now also generate NewBlock events.

Testing Done

  • follow_forks_pruned_block and follow_report_multiple_pruned_block cover the issues described above
  • created a loop of cargo test together with stress-ng --cpu 8 --cpu-load 100 --io 4 to filter out any flakiness

Review Notes

The entry point of the follow logic is moved to a dedicated file. While at it, have also simplified the notification stream logic:

/// Generate the block events for the `chainHead_follow` method.
pub async fn generate_events(

The blocks to ignore as pruned are collected initially here:

/// Get the in-memory blocks of the client, starting from the provided finalized hash.
fn get_init_blocks_with_forks(

Both issues are handled by this method:

/// Get all pruned block hashes from the provided stale heads.
///
/// The result does not include hashes from `to_ignore`.
fn get_pruned_hashes(

Additionally, the finalized stream could also generate the NewBlock events here:

// If the finalized hashes were not reported yet, generate the `NewBlock` events.
let mut events = self.generate_finalized_events(&finalized_block_hashes)?;

The other logic is moved from the chain_head to the chain_head_follow with minimal additions (mostly better error reporting).

Thanks @skunert and @davxy for the initial feedback and for raising the flakiness of the tests after the fix; plus the stale heads!

// CC: @paritytech/tools-team

@lexnv lexnv added A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes D3-trivial 🧸 PR contains trivial changes in a runtime directory that do not require an audit labels Feb 13, 2023
@lexnv lexnv self-assigned this Feb 13, 2023
Signed-off-by: Alexandru Vasile <[email protected]>
@lexnv lexnv requested review from bkchr and skunert February 14, 2023 09:21
Copy link
Contributor

@skunert skunert left a comment

Choose a reason for hiding this comment

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

Logic looks good to me, just some nits.

client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
@davxy
Copy link
Member

davxy commented Feb 14, 2023

Looks like some tests are not passing on this branch but are passing on master (at least "in my machine™️")
(also tried with single thread)
The failures are not deterministic... sometime some of the following are passing:

    chain_head::tests::get_body
    chain_head::tests::get_header
    chain_head::tests::get_storage
    chain_head::tests::get_storage_wrong_key
    chain_head::tests::call_runtime

Signed-off-by: Alexandru Vasile <[email protected]>
Copy link
Member

@davxy davxy left a comment

Choose a reason for hiding this comment

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

Not 100% familiar with this code part. But I've done my best 😁

Mostly looks good to me. Green light after the review points are resolved (or justified)

client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head.rs Outdated Show resolved Hide resolved
client/rpc-spec-v2/src/chain_head/chain_head_follow.rs Outdated Show resolved Hide resolved
@davxy davxy requested a review from a team February 28, 2023 19:06
Copy link
Contributor

@melekes melekes left a comment

Choose a reason for hiding this comment

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

lgtm minus comments above

lexnv and others added 14 commits March 1, 2023 19:51
Signed-off-by: Alexandru Vasile <[email protected]>
Signed-off-by: Alexandru Vasile <[email protected]>
@lexnv lexnv requested review from davxy and melekes March 3, 2023 15:14
Copy link
Member

@davxy davxy left a comment

Choose a reason for hiding this comment

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

Looks good

@lexnv lexnv merged commit 4d8f3d5 into master Mar 7, 2023
@lexnv lexnv deleted the lexnv/chainhead_prunned_blocks branch March 7, 2023 09:56
ukint-vs pushed a commit to gear-tech/substrate that referenced this pull request Apr 10, 2023
* rpc/chainhead: Test unpin for noncanonical prunned blocks

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Ensure fork is not reported by the Finalized event

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chainhead: Detect pruned forks to ignore from events

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Check unpin can be called on pruned hashes

Signed-off-by: Alexandru Vasile <[email protected]>

* Fix clippy

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Handle race with memory blocks and notifications

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Add data config for the `follow` future

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Address feedback

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Move best block cache on the data config

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Send new events from the finalized stream

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chian_head: Report all pruned blocks

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Move `chainHead_follow` logic on dedicated file

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Delegate follow logic to `chain_head_follow`

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Remove subscriptions on drop

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Ignore pruned blocks for a longer fork

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Check all pruned blocks are reported, not just stale heads

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Remove println debug and fix indentation

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Remove unnecessary trait bounds

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Add debug log for pruned forks

Signed-off-by: Alexandru Vasile <[email protected]>

* Revert "rpc/chain_head: Add debug log for pruned forks"

This reverts commit 425d6e7.

* Adjust blockID for testing

Signed-off-by: Alexandru Vasile <[email protected]>

* Update client/rpc-spec-v2/src/chain_head/chain_head_follow.rs

Co-authored-by: Davide Galassi <[email protected]>

* rpc/chain_head: Rename `ChainHeadFollow` to `ChainHeadFollower`

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Remove subscriptions manually

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Improve log messages by adding subID and errors

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Ensure `follow` stops sending events on first error

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Use default constructor

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Add `StartupPoint` structure

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Rename `in_memory_blocks`

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Fix comment typo

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Keep unique blocks and remove itertools

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Make sure `bestBlocks` events are generated in order

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Maintain order of reported blocks

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Parent of finalized block could be unpinned

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Fix warning

Signed-off-by: Alexandru Vasile <[email protected]>

---------

Signed-off-by: Alexandru Vasile <[email protected]>
Co-authored-by: Davide Galassi <[email protected]>
nathanwhit pushed a commit to nathanwhit/substrate that referenced this pull request Jul 19, 2023
* rpc/chainhead: Test unpin for noncanonical prunned blocks

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Ensure fork is not reported by the Finalized event

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chainhead: Detect pruned forks to ignore from events

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Check unpin can be called on pruned hashes

Signed-off-by: Alexandru Vasile <[email protected]>

* Fix clippy

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Handle race with memory blocks and notifications

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Add data config for the `follow` future

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Address feedback

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Move best block cache on the data config

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Send new events from the finalized stream

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chian_head: Report all pruned blocks

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Move `chainHead_follow` logic on dedicated file

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Delegate follow logic to `chain_head_follow`

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Remove subscriptions on drop

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Ignore pruned blocks for a longer fork

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Check all pruned blocks are reported, not just stale heads

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/tests: Remove println debug and fix indentation

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Remove unnecessary trait bounds

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Add debug log for pruned forks

Signed-off-by: Alexandru Vasile <[email protected]>

* Revert "rpc/chain_head: Add debug log for pruned forks"

This reverts commit 425d6e7.

* Adjust blockID for testing

Signed-off-by: Alexandru Vasile <[email protected]>

* Update client/rpc-spec-v2/src/chain_head/chain_head_follow.rs

Co-authored-by: Davide Galassi <[email protected]>

* rpc/chain_head: Rename `ChainHeadFollow` to `ChainHeadFollower`

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Remove subscriptions manually

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Improve log messages by adding subID and errors

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Ensure `follow` stops sending events on first error

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Use default constructor

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Add `StartupPoint` structure

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Rename `in_memory_blocks`

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Fix comment typo

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Keep unique blocks and remove itertools

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Make sure `bestBlocks` events are generated in order

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Maintain order of reported blocks

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Parent of finalized block could be unpinned

Signed-off-by: Alexandru Vasile <[email protected]>

* rpc/chain_head: Fix warning

Signed-off-by: Alexandru Vasile <[email protected]>

---------

Signed-off-by: Alexandru Vasile <[email protected]>
Co-authored-by: Davide Galassi <[email protected]>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes D3-trivial 🧸 PR contains trivial changes in a runtime directory that do not require an audit
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants