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

Less strict engine QoS timer #4411

Merged
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Additions and Improvements
- Allow free gas networks in the London fee market [#4061](https://github.com/hyperledger/besu/issues/4061)
- Upgrade besu-native to 0.6.0 and use Blake2bf native implementation if available by default [#4264](https://github.com/hyperledger/besu/pull/4264)
- Resets engine QoS timer with every call to the engine API instead of only when ExchangeTransitionConfiguration is called [#4411](https://github.com/hyperledger/besu/issues/4411)
- ExchangeTransitionConfiguration mismatch will only submit a debug log not a warning anymore [#4411](https://github.com/hyperledger/besu/issues/4411)

### Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hyperledger.besu.consensus.merge.MergeContext;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineCallListener;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
Expand Down Expand Up @@ -45,12 +46,17 @@ public enum EngineStatus {
protected final Optional<MergeContext> mergeContextOptional;
protected final Supplier<MergeContext> mergeContext;
protected final ProtocolContext protocolContext;
protected final EngineCallListener engineCallListener;

protected ExecutionEngineJsonRpcMethod(final Vertx vertx, final ProtocolContext protocolContext) {
protected ExecutionEngineJsonRpcMethod(
final Vertx vertx,
final ProtocolContext protocolContext,
final EngineCallListener engineCallListener) {
this.syncVertx = vertx;
this.protocolContext = protocolContext;
this.mergeContextOptional = protocolContext.safeConsensusContext(MergeContext.class);
this.mergeContext = mergeContextOptional::orElseThrow;
this.engineCallListener = engineCallListener;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.api.jsonrpc.internal.methods.engine;

public interface EngineCallListener {
void executionEngineCalled();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.QosTimer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EngineExchangeTransitionConfigurationParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
Expand All @@ -46,21 +45,11 @@ public class EngineExchangeTransitionConfiguration extends ExecutionEngineJsonRp
Difficulty.fromHexString(
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00");

static final long QOS_TIMEOUT_MILLIS = 120000L;

private final QosTimer qosTimer;

public EngineExchangeTransitionConfiguration(
final Vertx vertx, final ProtocolContext protocolContext) {
super(vertx, protocolContext);
qosTimer =
new QosTimer(
vertx,
QOS_TIMEOUT_MILLIS,
lastCall ->
LOG.warn(
"not called in {} seconds, consensus client may not be connected",
QOS_TIMEOUT_MILLIS / 1000L));
final Vertx vertx,
final ProtocolContext protocolContext,
final EngineCallListener engineCallListener) {
super(vertx, protocolContext, engineCallListener);
}

@Override
Expand All @@ -70,8 +59,7 @@ public String getName() {

@Override
public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) {
// update our QoS "last call time"
getQosTimer().resetTimer();
engineCallListener.executionEngineCalled();
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor point, we can DRY these up by adding this call into ExecutionEngineJsonRpcMethod.response() method rather than having it each engine api endpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason why I did not do that, was because of the unit tests. When I move it there, I am not sure if I can still test the correct behavior with the unit tests. Do you think it would still be possible?

Fabio had the same question:
#4411 (comment)


final EngineExchangeTransitionConfigurationParameter remoteTransitionConfiguration =
requestContext.getRequiredParameter(
Expand All @@ -97,7 +85,7 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
if (!localTransitionConfiguration
.getTerminalTotalDifficulty()
.equals(remoteTransitionConfiguration.getTerminalTotalDifficulty())) {
LOG.warn(
LOG.debug(
"Configured terminal total difficulty {} does not match value of consensus client {}",
localTransitionConfiguration.getTerminalTotalDifficulty(),
remoteTransitionConfiguration.getTerminalTotalDifficulty());
Expand All @@ -106,7 +94,7 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
if (!localTransitionConfiguration
.getTerminalBlockHash()
.equals(remoteTransitionConfiguration.getTerminalBlockHash())) {
LOG.warn(
LOG.debug(
"Configured terminal block hash {} does not match value of consensus client {}",
localTransitionConfiguration.getTerminalBlockHash(),
remoteTransitionConfiguration.getTerminalBlockHash());
Expand All @@ -128,9 +116,4 @@ private JsonRpcResponse respondWith(
final EngineExchangeTransitionConfigurationResult transitionConfiguration) {
return new JsonRpcSuccessResponse(requestId, transitionConfiguration);
}

// QosTimer accessor for testing considerations
QosTimer getQosTimer() {
return qosTimer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ public class EngineForkchoiceUpdated extends ExecutionEngineJsonRpcMethod {
public EngineForkchoiceUpdated(
final Vertx vertx,
final ProtocolContext protocolContext,
final MergeMiningCoordinator mergeCoordinator) {
super(vertx, protocolContext);
final MergeMiningCoordinator mergeCoordinator,
final EngineCallListener engineCallListener) {
super(vertx, protocolContext, engineCallListener);
this.mergeCoordinator = mergeCoordinator;
}

Expand All @@ -62,6 +63,8 @@ public String getName() {

@Override
public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) {
engineCallListener.executionEngineCalled();

final Object requestId = requestContext.getRequest().getId();

final EngineForkchoiceUpdatedParameter forkChoice =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ public class EngineGetPayload extends ExecutionEngineJsonRpcMethod {
public EngineGetPayload(
final Vertx vertx,
final ProtocolContext protocolContext,
final BlockResultFactory blockResultFactory) {
super(vertx, protocolContext);
final BlockResultFactory blockResultFactory,
final EngineCallListener engineCallListener) {
super(vertx, protocolContext, engineCallListener);
this.blockResultFactory = blockResultFactory;
}

Expand All @@ -55,6 +56,8 @@ public String getName() {

@Override
public JsonRpcResponse syncResponse(final JsonRpcRequestContext request) {
engineCallListener.executionEngineCalled();

final PayloadIdentifier payloadId = request.getRequiredParameter(0, PayloadIdentifier.class);

final Optional<Block> block = mergeContext.get().retrieveBlockById(payloadId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ public EngineNewPayload(
final Vertx vertx,
final ProtocolContext protocolContext,
final MergeMiningCoordinator mergeCoordinator,
final EthPeers ethPeers) {
super(vertx, protocolContext);
final EthPeers ethPeers,
final EngineCallListener engineCallListener) {
super(vertx, protocolContext, engineCallListener);
this.mergeCoordinator = mergeCoordinator;
this.ethPeers = ethPeers;
}
Expand All @@ -86,6 +87,8 @@ public String getName() {

@Override
public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext) {
engineCallListener.executionEngineCalled();

final EnginePayloadParameter blockParam =
requestContext.getRequiredParameter(0, EnginePayloadParameter.class);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.api.jsonrpc.internal.methods.engine;

import org.hyperledger.besu.ethereum.api.jsonrpc.internal.QosTimer;

import com.google.common.annotations.VisibleForTesting;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EngineQosTimer implements EngineCallListener {
static final long QOS_TIMEOUT_MILLIS = 120000L;
private static final Logger LOG = LoggerFactory.getLogger(EngineQosTimer.class);

private final QosTimer qosTimer;

public EngineQosTimer(final Vertx vertx) {
qosTimer = new QosTimer(vertx, QOS_TIMEOUT_MILLIS, lastCall -> logTimeoutWarning());
qosTimer.resetTimer();
}

@Override
public void executionEngineCalled() {
getQosTimer().resetTimer();
}

public void logTimeoutWarning() {
LOG.warn(
"Execution engine not called in {} seconds, consensus client may not be connected",
QOS_TIMEOUT_MILLIS / 1000L);
}

@VisibleForTesting
public QosTimer getQosTimer() {
return qosTimer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineForkchoiceUpdated;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineGetPayload;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineNewPayload;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.EngineQosTimer;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
Expand Down Expand Up @@ -62,17 +63,26 @@ protected String getApiGroup() {

@Override
protected Map<String, JsonRpcMethod> create() {
final EngineQosTimer engineQosTimer = new EngineQosTimer(consensusEngineServer);

if (mergeCoordinator.isPresent()) {
return mapOf(
new EngineGetPayload(consensusEngineServer, protocolContext, blockResultFactory),
new EngineGetPayload(
consensusEngineServer, protocolContext, blockResultFactory, engineQosTimer),
new EngineNewPayload(
consensusEngineServer, protocolContext, mergeCoordinator.get(), ethPeers),
consensusEngineServer,
protocolContext,
mergeCoordinator.get(),
ethPeers,
engineQosTimer),
new EngineForkchoiceUpdated(
consensusEngineServer, protocolContext, mergeCoordinator.get()),
new EngineExchangeTransitionConfiguration(consensusEngineServer, protocolContext));
consensusEngineServer, protocolContext, mergeCoordinator.get(), engineQosTimer),
new EngineExchangeTransitionConfiguration(
consensusEngineServer, protocolContext, engineQosTimer));
} else {
return mapOf(
new EngineExchangeTransitionConfiguration(consensusEngineServer, protocolContext));
new EngineExchangeTransitionConfiguration(
consensusEngineServer, protocolContext, engineQosTimer));
}
}
}
Loading