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

Clean up pivot selector from peers and unit test #8020

Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
487687d
7582: Add waitForPeer method to PeerSelector and EthPeers
Matilda-Clerke Dec 9, 2024
5e4cb9c
7582: Replace all usages of WaitForPeer[s]Task with new EthPeers.wait…
Matilda-Clerke Dec 9, 2024
337b900
7582: Fix PivotBlockConfirmerTest
Matilda-Clerke Dec 9, 2024
14e2ab8
7582: spotless
Matilda-Clerke Dec 9, 2024
6ce4741
7582: Fix broken PivotBlockRetrieverTest
Matilda-Clerke Dec 10, 2024
77a79e2
7582: Fix broken FastSyncActionsTest
Matilda-Clerke Dec 11, 2024
a841fb3
7582: spotless
Matilda-Clerke Dec 11, 2024
4654046
Merge branch 'refs/heads/replace-waitforpeertask-with-ethpeers-method…
Matilda-Clerke Dec 11, 2024
f4bae64
7582: Simplify PivotSelectorFromPeers.selectNewPivotBlock and add uni…
Matilda-Clerke Dec 11, 2024
56dbd9a
Merge branch 'refs/heads/main' into replace-waitforpeertask-with-ethp…
Matilda-Clerke Dec 11, 2024
0c2e9c2
Merge branch 'refs/heads/main' into replace-waitforpeertask-with-ethp…
Matilda-Clerke Dec 11, 2024
abc4cb4
7582: Fix issues after merge
Matilda-Clerke Dec 12, 2024
b3523b0
Merge branch 'refs/heads/replace-waitforpeertask-with-ethpeers-method…
Matilda-Clerke Dec 12, 2024
beeca44
7582: Put AbstractSyncTargetManager.waitForPeerAndThenSetSyncTarget c…
Matilda-Clerke Dec 13, 2024
215813d
7582: Remove pivot block checks when waiting for peer in FastSyncActions
Matilda-Clerke Dec 13, 2024
8c0df49
7582: Remove estimated chain height check from PivotBlockConfirmer wh…
Matilda-Clerke Dec 13, 2024
0bfb774
7582: Fix broken PivotBlockRetrieverTest
Matilda-Clerke Dec 15, 2024
06e6f1d
Merge branch 'main' into replace-waitforpeertask-with-ethpeers-method
Matilda-Clerke Dec 15, 2024
d08f4eb
Merge branch 'refs/heads/replace-waitforpeertask-with-ethpeers-method…
Matilda-Clerke Dec 15, 2024
652b46e
Fix compile errors
Matilda-Clerke Dec 15, 2024
17cf558
spotless
Matilda-Clerke Dec 15, 2024
2b46485
Merge branch 'refs/heads/main' into clean-up-PivotSelectorFromPeers-a…
Matilda-Clerke Dec 18, 2024
2b8d7d4
Refactor mockito usage
Matilda-Clerke Dec 19, 2024
c2855d2
Merge branch 'main' into clean-up-PivotSelectorFromPeers-and-unit-test
Matilda-Clerke Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.hyperledger.besu.ethereum.eth.sync.TrailingPeerRequirements;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

Expand Down Expand Up @@ -86,41 +87,24 @@ protected Optional<FastSyncState> fromBestPeer(final EthPeer peer) {
}

protected Optional<EthPeer> selectBestPeer() {
return ethContext
.getEthPeers()
.bestPeerMatchingCriteria(this::canPeerDeterminePivotBlock)
// Only select a pivot block number when we have a minimum number of height estimates
.filter(unused -> enoughFastSyncPeersArePresent());
}

private boolean enoughFastSyncPeersArePresent() {
final long peerCount = countPeersThatCanDeterminePivotBlock();
List<EthPeer> peers =
ethContext
.getEthPeers()
.streamAvailablePeers()
.filter((peer) -> peer.chainState().hasEstimatedHeight() && peer.isFullyValidated())
.toList();

// Only select a pivot block number when we have a minimum number of height estimates
final int minPeerCount = syncConfig.getSyncMinimumPeerCount();
if (peerCount < minPeerCount) {
if (peers.size() < minPeerCount) {
LOG.info(
"Waiting for valid peers with chain height information. {} / {} required peers currently available.",
peerCount,
peers.size(),
minPeerCount);
return false;
return Optional.empty();
} else {
return peers.stream().max(ethContext.getEthPeers().getBestPeerComparator());
}
return true;
}

private long countPeersThatCanDeterminePivotBlock() {
return ethContext
.getEthPeers()
.streamAvailablePeers()
.filter(this::canPeerDeterminePivotBlock)
.count();
}

private boolean canPeerDeterminePivotBlock(final EthPeer peer) {
LOG.debug(
Copy link
Contributor

Choose a reason for hiding this comment

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

we're losing this log line, is that intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not a particularly good log as it doesn't include any context and is likely to end up listing peers. I can look to add something similar if you think it's worthwhile

Copy link
Contributor

Choose a reason for hiding this comment

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

all good just checking it won't be missed

"peer {} hasEstimatedHeight {} isFullyValidated? {}",
peer.getLoggableId(),
peer.chainState().hasEstimatedHeight(),
peer.isFullyValidated());
return peer.chainState().hasEstimatedHeight() && peer.isFullyValidated();
}

private long conservativelyEstimatedPivotBlock() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.eth.sync.fastsync;

import org.hyperledger.besu.ethereum.eth.manager.ChainState;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;

import java.util.Optional;
import java.util.stream.Stream;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class PivotSelectorFromPeersTest {
private @Mock EthContext ethContext;
private @Mock EthPeers ethPeers;
private @Mock SyncState syncState;

private PivotSelectorFromPeers selector;

@BeforeEach
public void beforeTest() {
SynchronizerConfiguration syncConfig =
SynchronizerConfiguration.builder().syncMinimumPeerCount(2).syncPivotDistance(1).build();

selector = new PivotSelectorFromPeers(ethContext, syncConfig, syncState);
}

@Test
public void testSelectNewPivotBlock() {
EthPeer peer1 = mockPeer(true, 10, true);
EthPeer peer2 = mockPeer(true, 8, true);

Mockito.when(ethContext.getEthPeers()).thenReturn(ethPeers);
Mockito.when(ethPeers.streamAvailablePeers()).thenReturn(Stream.of(peer1, peer2));
Mockito.when(ethPeers.getBestPeerComparator())
.thenReturn((p1, ignored) -> p1 == peer1 ? 1 : -1);

Optional<FastSyncState> result = selector.selectNewPivotBlock();

Assertions.assertTrue(result.isPresent());
Assertions.assertEquals(9, result.get().getPivotBlockNumber().getAsLong());
}

@Test
public void testSelectNewPivotBlockWithInsufficientPeers() {
Mockito.when(ethContext.getEthPeers()).thenReturn(ethPeers);
Mockito.when(ethPeers.streamAvailablePeers()).thenReturn(Stream.empty());

Optional<FastSyncState> result = selector.selectNewPivotBlock();

Assertions.assertTrue(result.isEmpty());
}

private EthPeer mockPeer(
final boolean hasEstimatedHeight, final long chainHeight, final boolean isFullyValidated) {
EthPeer ethPeer = Mockito.mock(EthPeer.class);
ChainState chainState = Mockito.mock(ChainState.class);
Mockito.when(ethPeer.chainState()).thenReturn(chainState);
Mockito.when(chainState.hasEstimatedHeight()).thenReturn(hasEstimatedHeight);
Mockito.when(chainState.getEstimatedHeight()).thenReturn(chainHeight);
Mockito.when(ethPeer.isFullyValidated()).thenReturn(isFullyValidated);

return ethPeer;
}
}
Loading