mirror of https://github.com/hyperledger/besu
Expand readiness check to check peer count and sync state (#1568)
Query params can be used to adjust the number of peers required and number of blocks tolerance to be considered in sync. Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
cee72c8590
commit
8bf224707f
@ -0,0 +1,82 @@ |
||||
/* |
||||
* Copyright 2018 ConsenSys AG. |
||||
* |
||||
* 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.jsonrpc.health; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.SyncStatus; |
||||
import tech.pegasys.pantheon.ethereum.core.Synchronizer; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.health.HealthService.HealthCheck; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.health.HealthService.ParamSource; |
||||
import tech.pegasys.pantheon.ethereum.p2p.network.P2PNetwork; |
||||
|
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
public class ReadinessCheck implements HealthCheck { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
private static final int DEFAULT_MINIMUM_PEERS = 1; |
||||
private static final int DEFAULT_MAX_BLOCKS_BEHIND = 2; |
||||
private final P2PNetwork p2pNetwork; |
||||
private final Synchronizer synchronizer; |
||||
|
||||
public ReadinessCheck(final P2PNetwork p2pNetwork, final Synchronizer synchronizer) { |
||||
this.p2pNetwork = p2pNetwork; |
||||
this.synchronizer = synchronizer; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isHealthy(final ParamSource params) { |
||||
LOG.debug("Invoking readiness check."); |
||||
if (p2pNetwork.isP2pEnabled()) { |
||||
final int peerCount = p2pNetwork.getPeerCount(); |
||||
if (!hasMinimumPeers(params, peerCount)) { |
||||
return false; |
||||
} |
||||
} |
||||
return synchronizer |
||||
.getSyncStatus() |
||||
.map(syncStatus -> isInSync(syncStatus, params)) |
||||
.orElse(true); |
||||
} |
||||
|
||||
private boolean isInSync(final SyncStatus syncStatus, final ParamSource params) { |
||||
final String maxBlocksBehindParam = params.getParam("maxBlocksBehind"); |
||||
final long maxBlocksBehind; |
||||
if (maxBlocksBehindParam == null) { |
||||
maxBlocksBehind = DEFAULT_MAX_BLOCKS_BEHIND; |
||||
} else { |
||||
try { |
||||
maxBlocksBehind = Long.parseLong(maxBlocksBehindParam); |
||||
} catch (final NumberFormatException e) { |
||||
LOG.debug("Invalid maxBlocksBehind: {}. Reporting as not ready.", maxBlocksBehindParam); |
||||
return false; |
||||
} |
||||
} |
||||
return syncStatus.getHighestBlock() - syncStatus.getCurrentBlock() <= maxBlocksBehind; |
||||
} |
||||
|
||||
private boolean hasMinimumPeers(final ParamSource params, final int peerCount) { |
||||
final int minimumPeers; |
||||
final String peersParam = params.getParam("minPeers"); |
||||
if (peersParam == null) { |
||||
minimumPeers = DEFAULT_MINIMUM_PEERS; |
||||
} else { |
||||
try { |
||||
minimumPeers = Integer.parseInt(peersParam); |
||||
} catch (final NumberFormatException e) { |
||||
LOG.debug("Invalid minPeers: {}. Reporting as not ready.", peersParam); |
||||
return false; |
||||
} |
||||
} |
||||
return peerCount >= minimumPeers; |
||||
} |
||||
} |
@ -1,36 +0,0 @@ |
||||
/* |
||||
* Copyright 2018 ConsenSys AG. |
||||
* |
||||
* 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.jsonrpc.health; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.p2p.network.P2PNetwork; |
||||
|
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
|
||||
public class ReadinessService extends HealthService { |
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
public ReadinessService(final P2PNetwork p2PNetwork) { |
||||
super(buildHealthCheck(p2PNetwork)); |
||||
} |
||||
|
||||
private static HealthCheck buildHealthCheck(final P2PNetwork p2PNetwork) { |
||||
return () -> { |
||||
LOG.debug("Invoking readiness service."); |
||||
if (p2PNetwork.isP2pEnabled()) { |
||||
return p2PNetwork.isListening(); |
||||
} |
||||
return true; |
||||
}; |
||||
} |
||||
} |
@ -0,0 +1,146 @@ |
||||
/* |
||||
* Copyright 2019 ConsenSys AG. |
||||
* |
||||
* 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.jsonrpc.health; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.SyncStatus; |
||||
import tech.pegasys.pantheon.ethereum.core.Synchronizer; |
||||
import tech.pegasys.pantheon.ethereum.jsonrpc.health.HealthService.ParamSource; |
||||
import tech.pegasys.pantheon.ethereum.p2p.network.P2PNetwork; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
import java.util.Optional; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
public class ReadinessCheckTest { |
||||
|
||||
private static final String MIN_PEERS_PARAM = "minPeers"; |
||||
private final P2PNetwork p2pNetwork = mock(P2PNetwork.class); |
||||
private final Synchronizer synchronizer = mock(Synchronizer.class); |
||||
|
||||
private final Map<String, String> params = new HashMap<>(); |
||||
private final ParamSource paramSource = params::get; |
||||
|
||||
private final ReadinessCheck readinessCheck = new ReadinessCheck(p2pNetwork, synchronizer); |
||||
|
||||
@Test |
||||
public void shouldBeReadyWhenDefaultLimitsUsedAndReached() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(1); |
||||
when(synchronizer.getSyncStatus()).thenReturn(Optional.empty()); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldBeReadyWithNoPeersWhenP2pIsDisabled() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(false); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(0); |
||||
when(synchronizer.getSyncStatus()).thenReturn(Optional.empty()); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldBeReadyWithNoPeersWhenMinimumPeersSetToZero() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(0); |
||||
when(synchronizer.getSyncStatus()).thenReturn(Optional.empty()); |
||||
|
||||
params.put(MIN_PEERS_PARAM, "0"); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotBeReadyWhenIncreasedMinimumPeersNotReached() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(5); |
||||
when(synchronizer.getSyncStatus()).thenReturn(Optional.empty()); |
||||
|
||||
params.put(MIN_PEERS_PARAM, "10"); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotBeReadyWhenMinimumPeersParamIsInvalid() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(500); |
||||
when(synchronizer.getSyncStatus()).thenReturn(Optional.empty()); |
||||
|
||||
params.put(MIN_PEERS_PARAM, "abc"); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldBeReadyWhenLessThanDefaultMaxBlocksBehind() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(5); |
||||
when(synchronizer.getSyncStatus()).thenReturn(createSyncStatus(1000, 1002)); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotBeReadyWhenLessThanDefaultMaxBlocksBehind() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(5); |
||||
when(synchronizer.getSyncStatus()).thenReturn(createSyncStatus(1000, 1003)); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldBeReadyWhenLessThanCustomMaxBlocksBehind() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(5); |
||||
when(synchronizer.getSyncStatus()).thenReturn(createSyncStatus(500, 600)); |
||||
|
||||
params.put("maxBlocksBehind", "100"); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotBeReadyWhenGreaterThanCustomMaxBlocksBehind() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(5); |
||||
when(synchronizer.getSyncStatus()).thenReturn(createSyncStatus(500, 601)); |
||||
|
||||
params.put("maxBlocksBehind", "100"); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldNotBeReadyWhenCustomMaxBlocksBehindIsInvalid() { |
||||
when(p2pNetwork.isP2pEnabled()).thenReturn(true); |
||||
when(p2pNetwork.getPeerCount()).thenReturn(5); |
||||
when(synchronizer.getSyncStatus()).thenReturn(createSyncStatus(500, 500)); |
||||
|
||||
params.put("maxBlocksBehind", "abc"); |
||||
|
||||
assertThat(readinessCheck.isHealthy(paramSource)).isFalse(); |
||||
} |
||||
|
||||
private Optional<SyncStatus> createSyncStatus(final int currentBlock, final int highestBlock) { |
||||
return Optional.of(new SyncStatus(0, currentBlock, highestBlock)); |
||||
} |
||||
} |
Loading…
Reference in new issue