Skip to content

refactor synchronizer#2433

Open
turuslan wants to merge 27 commits into
masterfrom
refactor/synchronizer
Open

refactor synchronizer#2433
turuslan wants to merge 27 commits into
masterfrom
refactor/synchronizer

Conversation

@turuslan

Copy link
Copy Markdown
Contributor

Referenced issues

Description of the Change

  • should also fix [Bug]: Ancestry is never removed #2420 ancestry_ not removed, each KnownBlock stores it's ancestry_ iterator
  • separate fetchGrandpaFork function
  • remove syncState peer arg, simplify syncState callback
  • remove syncByBlockInfo/syncByBlockHeader ambigous callback
  • use NewHead event in timeline to check catch up status
  • checkExtrinsicRoot
  • check --max-parallel-downloads at least 1

Sync

  • types of blocks
    • finalized canonical - in db
    • finalized forks - should be discarded
    • unfinalized - in db
    • "attached" - in memory, received headers
      • headers form multiple trees
      • root parent is unfinalized or last finalized block
      • root will be executed (full sync) or appended (fast sync) next
      • ready to be imported, may need to fetch body to execute (full sync)
      • when sync doesn't lag, received block announce is attached
    • "detached" - in memory, received headers
      • headers form multiple trees
      • root parent has no known parent
      • fetch more headers down from root to reach known blocks, merge with detached or attached, or become attached
      • when sync lags, received block announce is detached
  • types of requests
    • "gap" - fetch headers descending parent chain to known blocks
    • "body" - fetch body to execute during full sync
    • "range" - fetch peer best chain, by number, ascending from top
      • useful when there are no block announces or they are too far
      • optimistic version of "find common block"
        • if peer chain is not fork we insert that chain and continue fetching more from new max
        • even if that chain is fork, we fetch more from new max, and fetch "gap" when that chain diverges
  • synchronizer knows attached/detached roots of known blocks trees, so it can plan and prefetch header/body from different peers in parallel

Possible Drawbacks

  • validate seal and vrf for current and next session

Signed-off-by: turuslan <turuslan.devbox@gmail.com>
@turuslan turuslan requested review from kamilsa and xDimon April 10, 2025 15:09
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
@kamilsa kamilsa requested a review from Copilot April 11, 2025 05:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@kamilsa kamilsa requested a review from Copilot April 11, 2025 06:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot reviewed 12 out of 13 changed files in this pull request and generated no comments.

Files not reviewed (1)
  • test/core/network/CMakeLists.txt: Language not supported
Comments suppressed due to low confidence (3)

test/mock/core/network/synchronizer_mock.hpp:19

  • [nitpick] The refactor removes the boolean-based syncByBlockInfo and syncByBlockHeader methods in favor of void-returning notification methods. Please ensure that all tests and mocks depending on the old behavior are updated to use the new interface.
MOCK_METHOD(void, onBlockAnnounceHandshake, (const BlockInfo &, const PeerId &), (override));

test/core/network/synchronizer_test.cpp:1

  • The entire synchronizer test file has been removed. Confirm that synchronizer-related functionality remains adequately exercised by other tests or that this removal is intentional.
/* Entire file removed */

core/network/impl/synchronizer_impl.hpp:271

  • [nitpick] The new variables attached_roots_ and detached_roots_ are central to managing block roots. Consider adding inline documentation clarifying their specific roles and how they relate to the ancestry structure.
std::set<BlockInfo> attached_roots_;

Comment thread core/network/synchronizer.hpp Outdated
Comment thread core/network/impl/synchronizer_impl.hpp Outdated
Comment thread core/network/impl/synchronizer_impl.cpp
SL_WARN(log_, "sync hung up, restarting sync");
known_blocks_.clear();
generations_.clear();
attached_roots_.clear();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Some random thoughts. Maybe instead of relying on timer, it would be more straightforward to implement ttl containers for these data?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This isn't TTL, it's workaround timeout which resets synchronizer state if it hangs, which isn't normal situation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

so, in normal situation this container (and maybe others) will grow indefinetely?

Comment thread core/network/impl/synchronizer_impl.hpp Outdated
return VisitAncestryResult::STOP;
}
if (not block.data.body.has_value()) {
auto block_info = block.data.header.value().blockInfo();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not used

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Used in 3 lines below

auto remove_executing_blocks =
auto &known = known_blocks_.at(block_info.hash);
auto make_cb = [&] {
executing_blocks_.emplace(block_info);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

block_info is captured by reference. make_cb is invoked after block execution, which happens in separate thread if am not mistaken. Can't we end up with dangling reference to block_info ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

make_cb returns cb to use as arg to append/execute functions, so it's called before them

Comment thread core/network/impl/synchronizer_impl.hpp Outdated
Comment thread core/network/impl/synchronizer_impl.cpp Outdated
continue;
}
if (auto known = entry(known_blocks_, block.hash)) {
if (not checkExtrinsicRoot(known->data.header.value(), *block.body)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hopefully, we cannot skip extrinsic root validation if block was not known

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

loadBlocks doesn't insert body into known_blocks_,
it requires header with extrinsic root to be known.

kamilsa and others added 8 commits April 11, 2025 17:25
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
@turuslan turuslan requested a review from kamilsa April 14, 2025 16:08
Comment thread core/consensus/grandpa/impl/grandpa_impl.cpp Outdated
Comment thread core/network/impl/synchronizer_impl.hpp Outdated
Comment thread core/network/impl/synchronizer_impl.hpp Outdated
Comment thread core/network/impl/synchronizer_impl.hpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please, provide new set of tests for new mechanism of sync.

return;
}
return res;
fetchTasks();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems new unknown block will just be ignored.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Handshake contains only block hash and number, not header.

If block is not in known_blocks_, it can either be already in db (block tree) or new unknown block.

Comment thread core/network/impl/synchronizer_impl.cpp Outdated
auto cb = [WEAK_SELF, peer_id, request, reason, busy{std::move(busy)}](
outcome::result<BlocksResponse> response_res) mutable {
WEAK_LOCK(self);
busy.reset();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should fetching be decremented here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's decremented.
busy.reset() -> ~MovableFinalAction -> --fetching

from);
if (handler) {
handler(Error::WRONG_ORDER);
if (block.body.has_value()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we handle body in case of header absence?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We add body to known_blocks_ if header is already there.
If response contains both header and body, header is added to known_blocks_ first

Comment thread core/network/impl/synchronizer_impl.cpp
turuslan added 10 commits April 21, 2025 15:44
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
@turuslan turuslan requested a review from xDimon April 23, 2025 03:49
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
BlocksRequest request{
.fields = BlockAttribute::HEADER | BlockAttribute::JUSTIFICATION,
.from = root.hash,
.from = known_blocks_.at(root.hash).data.header.value().parent_hash,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

.at() potential source of exception. Does it guaranteed known_blocks_ contains root.hash? Use op[] if yes, or add check up otherwise.

@turuslan turuslan Apr 24, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

.at() was used intentionally, because std::unordered_map::operator[] doesn't assert.
known_blocks_, attached_roots_, detached_roots_, and ancestry_ are assumed to be consistent,
but assert/exception will protect from breaking that consistency.

Comment thread core/network/impl/synchronizer_impl.cpp Outdated
Comment on lines -1033 to -1035
if (request.direction == Direction::ASCENDING) {
std::ranges::reverse(blocks);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are you sure this should be removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was moved to other place

Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
Signed-off-by: turuslan <turuslan.devbox@gmail.com>

# Conflicts:
#	core/application/impl/app_configuration_impl.cpp
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
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.

[Bug]: Ancestry is never removed

4 participants