Skip to content

Commit c862af7

Browse files
Trigger primary BN readiness check when failover has issues (Consensys#8193)
1 parent 8ac4353 commit c862af7

File tree

3 files changed

+55
-19
lines changed

3 files changed

+55
-19
lines changed

validator/remote/src/main/java/tech/pegasys/teku/validator/remote/BeaconNodeReadinessManager.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ public Iterator<? extends RemoteValidatorApiChannel> getFailoversInOrderOfReadin
7777
.iterator();
7878
}
7979

80+
public SafeFuture<Void> performPrimaryReadinessCheck() {
81+
return performReadinessCheck(primaryBeaconNodeApi, true);
82+
}
83+
8084
@Override
8185
protected SafeFuture<?> doStart() {
8286
return performReadinessCheckAgainstAllNodes();
@@ -136,10 +140,6 @@ private SafeFuture<Void> performReadinessCheckAgainstAllNodes() {
136140
return SafeFuture.allOf(primaryReadinessCheck, SafeFuture.allOf(failoverReadinessChecks));
137141
}
138142

139-
private SafeFuture<Void> performPrimaryReadinessCheck() {
140-
return performReadinessCheck(primaryBeaconNodeApi, true);
141-
}
142-
143143
private SafeFuture<Void> performFailoverReadinessCheck(final RemoteValidatorApiChannel failover) {
144144
return performReadinessCheck(failover, false);
145145
}

validator/remote/src/main/java/tech/pegasys/teku/validator/remote/eventsource/EventSourceBeaconChainEventAdapter.java

+23-15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.ArrayList;
2727
import java.util.List;
2828
import java.util.Objects;
29+
import java.util.Optional;
2930
import java.util.concurrent.CountDownLatch;
3031
import java.util.concurrent.TimeUnit;
3132
import okhttp3.HttpUrl;
@@ -54,7 +55,7 @@ public class EventSourceBeaconChainEventAdapter
5455
private final CountDownLatch runningLatch = new CountDownLatch(1);
5556

5657
private volatile BackgroundEventSource eventSource;
57-
private volatile RemoteValidatorApiChannel currentBeaconNodeUsedForEventStreaming;
58+
@VisibleForTesting volatile RemoteValidatorApiChannel currentBeaconNodeUsedForEventStreaming;
5859

5960
private final BeaconNodeReadinessManager beaconNodeReadinessManager;
6061
private final RemoteValidatorApiChannel primaryBeaconNodeApi;
@@ -120,9 +121,14 @@ public void onPrimaryNodeNotReady() {
120121
}
121122

122123
@Override
124+
@SuppressWarnings("FutureReturnValueIgnored")
123125
public void onFailoverNodeNotReady(final RemoteValidatorApiChannel failoverNotInSync) {
124126
if (currentEventStreamHasSameEndpoint(failoverNotInSync)) {
125-
switchToFailoverEventStreamIfAvailable();
127+
if (failoverBeaconNodeApis.size() == 1 || !switchToFailoverEventStreamIfAvailable()) {
128+
// No failover switching is available, and we are currently connected to a failover node
129+
// with issues, so trigger the readiness check against the primary BN immediately
130+
beaconNodeReadinessManager.performPrimaryReadinessCheck();
131+
}
126132
}
127133
}
128134

@@ -170,26 +176,28 @@ private HttpUrl createEventStreamSourceUrl(
170176
}
171177

172178
// synchronized because of the ConnectionErrorHandler and the BeaconNodeReadinessChannel callbacks
173-
private synchronized void switchToFailoverEventStreamIfAvailable() {
179+
private synchronized boolean switchToFailoverEventStreamIfAvailable() {
174180
if (failoverBeaconNodeApis.isEmpty()) {
175-
return;
181+
return false;
176182
}
177-
findReadyFailoverAndSwitch();
183+
return findReadyFailoverAndSwitch();
178184
}
179185

180-
private void findReadyFailoverAndSwitch() {
181-
failoverBeaconNodeApis.stream()
182-
.filter(beaconNodeReadinessManager::isReady)
183-
.findFirst()
184-
.ifPresentOrElse(
185-
this::switchToFailoverEventStream,
186-
validatorLogger::noFailoverBeaconNodesAvailableForEventStreaming);
186+
private boolean findReadyFailoverAndSwitch() {
187+
final Optional<? extends RemoteValidatorApiChannel> readyFailover =
188+
failoverBeaconNodeApis.stream()
189+
.filter(beaconNodeReadinessManager::isReady)
190+
.filter(failover -> !currentEventStreamHasSameEndpoint(failover))
191+
.findFirst();
192+
if (readyFailover.isPresent()) {
193+
switchToFailoverEventStream(readyFailover.get());
194+
return true;
195+
}
196+
validatorLogger.noFailoverBeaconNodesAvailableForEventStreaming();
197+
return false;
187198
}
188199

189200
private void switchToFailoverEventStream(final RemoteValidatorApiChannel beaconNodeApi) {
190-
if (currentEventStreamHasSameEndpoint(beaconNodeApi)) {
191-
return;
192-
}
193201
eventSource.close();
194202
eventSource = createEventSource(beaconNodeApi);
195203
currentBeaconNodeUsedForEventStreaming = beaconNodeApi;

validator/remote/src/test/java/tech/pegasys/teku/validator/remote/eventsource/EventSourceBeaconChainEventAdapterTest.java

+28
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@
2121

2222
import java.net.URI;
2323
import java.util.ArrayList;
24+
import java.util.List;
2425
import java.util.stream.Stream;
2526
import okhttp3.HttpUrl;
2627
import okhttp3.OkHttpClient;
2728
import org.hyperledger.besu.plugin.services.MetricsSystem;
2829
import org.hyperledger.besu.plugin.services.metrics.Counter;
2930
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
3031
import org.junit.jupiter.api.BeforeEach;
32+
import org.junit.jupiter.api.Test;
3133
import org.junit.jupiter.params.ParameterizedTest;
3234
import org.junit.jupiter.params.provider.ValueSource;
3335
import tech.pegasys.teku.api.response.v1.EventType;
@@ -69,6 +71,32 @@ public void shouldSubscribeToSlashingEvents(final boolean shutdownWhenValidatorS
6971
verifyEventSourceSubscriptionUrl(httpUrlMock, shutdownWhenValidatorSlashedEnabled);
7072
}
7173

74+
@Test
75+
public void performsPrimaryReadinessCheckWhenFailoverNotReadyAndNoOtherFailoversAvailable() {
76+
final BeaconNodeReadinessManager beaconNodeReadinessManager =
77+
mock(BeaconNodeReadinessManager.class);
78+
final RemoteValidatorApiChannel failover = mock(RemoteValidatorApiChannel.class);
79+
final EventSourceBeaconChainEventAdapter eventSourceBeaconChainEventAdapter =
80+
new EventSourceBeaconChainEventAdapter(
81+
beaconNodeReadinessManager,
82+
mock(RemoteValidatorApiChannel.class),
83+
List.of(failover),
84+
mock(OkHttpClient.class),
85+
mock(ValidatorLogger.class),
86+
mock(BeaconChainEventAdapter.class),
87+
mock(ValidatorTimingChannel.class),
88+
metricsSystemMock,
89+
true,
90+
false,
91+
mock(Spec.class));
92+
93+
eventSourceBeaconChainEventAdapter.currentBeaconNodeUsedForEventStreaming = failover;
94+
95+
eventSourceBeaconChainEventAdapter.onFailoverNodeNotReady(failover);
96+
97+
verify(beaconNodeReadinessManager).performPrimaryReadinessCheck();
98+
}
99+
72100
private EventSourceBeaconChainEventAdapter initEventSourceBeaconChainEventAdapter(
73101
final boolean shutdownWhenValidatorSlashedEnabled) {
74102
return new EventSourceBeaconChainEventAdapter(

0 commit comments

Comments
 (0)