Skip to content

Commit

Permalink
feat(req-limit): implement request limiting for trace generation and …
Browse files Browse the repository at this point in the history
…line counting (#1241)


Signed-off-by: Tsvetan Dimitrov <[email protected]>
  • Loading branch information
powerslider authored Sep 23, 2024
1 parent fd5a92c commit cf6cef9
Show file tree
Hide file tree
Showing 24 changed files with 774 additions and 231 deletions.
3 changes: 2 additions & 1 deletion arithmetization/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ dependencies {

implementation 'info.picocli:picocli'

compileOnly 'io.vertx:vertx-core'
implementation 'io.vertx:vertx-core'
implementation 'io.vertx:vertx-web'

implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,7 @@ protected LineaOptionsPluginConfiguration getConfigurationByKey(final String key

@Override
public synchronized void register(final BesuContext context) {
final PicoCLIOptions cmdlineOptions =
context
.getService(PicoCLIOptions.class)
.orElseThrow(
() ->
new IllegalStateException(
"Failed to obtain PicoCLI options from the BesuContext"));
final PicoCLIOptions cmdlineOptions = BesuServiceProvider.getPicoCLIOptionsService(context);

getLineaPluginConfigMap()
.forEach(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.plugins.config.LineaL1L2BridgeSharedCliOptions;
import net.consensys.linea.plugins.config.LineaL1L2BridgeSharedConfiguration;
import net.consensys.linea.plugins.rpc.RpcCliOptions;
import net.consensys.linea.plugins.rpc.RpcConfiguration;

/** In this class we put CLI options that are shared with other plugins not defined here */
@Slf4j
Expand All @@ -29,16 +31,22 @@ public abstract class AbstractLineaSharedOptionsPlugin extends AbstractLineaOpti
public Map<String, LineaOptionsPluginConfiguration> getLineaPluginConfigMap() {
final LineaL1L2BridgeSharedCliOptions l1L2BridgeCliOptions =
LineaL1L2BridgeSharedCliOptions.create();
final RpcCliOptions rpcCliOptions = RpcCliOptions.create();

return Map.of(
LineaL1L2BridgeSharedCliOptions.CONFIG_KEY, l1L2BridgeCliOptions.asPluginConfig());
LineaL1L2BridgeSharedCliOptions.CONFIG_KEY, l1L2BridgeCliOptions.asPluginConfig(),
RpcCliOptions.CONFIG_KEY, rpcCliOptions.asPluginConfig());
}

public LineaL1L2BridgeSharedConfiguration l1L2BridgeSharedConfiguration() {
return (LineaL1L2BridgeSharedConfiguration)
getConfigurationByKey(LineaL1L2BridgeSharedCliOptions.CONFIG_KEY).optionsConfig();
}

public RpcConfiguration rpcConfiguration() {
return (RpcConfiguration) getConfigurationByKey(RpcCliOptions.CONFIG_KEY).optionsConfig();
}

@Override
public void start() {
super.start();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Consensys Software Inc.
*
* 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 net.consensys.linea.plugins;

import org.hyperledger.besu.plugin.BesuContext;
import org.hyperledger.besu.plugin.services.BesuService;
import org.hyperledger.besu.plugin.services.PicoCLIOptions;
import org.hyperledger.besu.plugin.services.RpcEndpointService;
import org.hyperledger.besu.plugin.services.TraceService;
import org.hyperledger.besu.plugin.services.sync.SynchronizationService;

public class BesuServiceProvider {

/**
* Initialize a service of type {@link BesuService}.
*
* @return the initialized {@link BesuService}.
*/
public static <T extends BesuService> T getBesuService(
final BesuContext context, final Class<T> clazz) {
return context
.getService(clazz)
.orElseThrow(
() ->
new RuntimeException(
"Unable to find given Besu service. Please ensure %s is registered."
.formatted(clazz.getName())));
}

public static TraceService getTraceService(final BesuContext context) {
return getBesuService(context, TraceService.class);
}

public static PicoCLIOptions getPicoCLIOptionsService(final BesuContext context) {
return getBesuService(context, PicoCLIOptions.class);
}

public static RpcEndpointService getRpcEndpointService(final BesuContext context) {
return getBesuService(context, RpcEndpointService.class);
}

public static SynchronizationService getSynchronizationService(final BesuContext context) {
return getBesuService(context, SynchronizationService.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.auto.service.AutoService;
import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.corset.CorsetValidator;
import net.consensys.linea.plugins.BesuServiceProvider;
import org.hyperledger.besu.plugin.BesuContext;
import org.hyperledger.besu.plugin.BesuPlugin;
import org.hyperledger.besu.plugin.services.BesuEvents;
Expand All @@ -45,13 +46,7 @@ public Optional<String> getName() {

@Override
public void register(final BesuContext context) {
final PicoCLIOptions cmdlineOptions =
context
.getService(PicoCLIOptions.class)
.orElseThrow(
() ->
new IllegalStateException(
"Expecting a PicoCLI options to register CLI options with, but none found."));
final PicoCLIOptions cmdlineOptions = BesuServiceProvider.getPicoCLIOptionsService(context);

cmdlineOptions.addPicoCLIOptions(getName().get(), options);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright Consensys Software Inc.
*
* 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 net.consensys.linea.plugins.readiness;

import com.google.common.base.MoreObjects;
import net.consensys.linea.plugins.LineaCliOptions;
import picocli.CommandLine;

public class TracerReadinessCliOptions implements LineaCliOptions {
public static final String CONFIG_KEY = "tracer-readiness";

static final String TRACER_READINESS_SERVER_HOST = "--plugin-linea-tracer-readiness-server-host";
static final String TRACER_READINESS_SERVER_PORT = "--plugin-linea-tracer-readiness-server-port";

@CommandLine.Option(
names = {TRACER_READINESS_SERVER_PORT},
hidden = true,
paramLabel = "<SERVER_PORT>",
description = "HTTP server port for tracer readiness plugin")
private int serverPort = 8548;

@CommandLine.Option(
names = {TRACER_READINESS_SERVER_HOST},
hidden = true,
paramLabel = "<SERVER_HOST>",
description = "HTTP server host for tracer readiness plugin")
private String serverHost = "0.0.0.0";

private TracerReadinessCliOptions() {}

/**
* Create Linea cli options.
*
* @return the Linea cli options
*/
public static TracerReadinessCliOptions create() {
return new TracerReadinessCliOptions();
}

/**
* Linea cli options from config.
*
* @param config the config
* @return the Linea cli options
*/
static TracerReadinessCliOptions fromConfig(final TracerReadinessConfiguration config) {
final TracerReadinessCliOptions options = create();
options.serverHost = config.serverHost();
options.serverPort = config.serverPort();
return options;
}

/**
* To domain object Linea factory configuration.
*
* @return the Linea factory configuration
*/
@Override
public TracerReadinessConfiguration toDomainObject() {
return TracerReadinessConfiguration.builder()
.serverHost(serverHost)
.serverPort(serverPort)
.build();
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add(TRACER_READINESS_SERVER_HOST, serverHost)
.add(TRACER_READINESS_SERVER_PORT, serverPort)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Consensys Software Inc.
*
* 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 net.consensys.linea.plugins.readiness;

import lombok.Builder;
import net.consensys.linea.plugins.LineaOptionsConfiguration;

/** The Linea tracer configuration private to this repo. */
@Builder(toBuilder = true)
public record TracerReadinessConfiguration(String serverHost, int serverPort)
implements LineaOptionsConfiguration {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright Consensys Software Inc.
*
* 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 net.consensys.linea.plugins.readiness;

import static java.util.Collections.singletonMap;

import java.util.Map;
import java.util.Optional;

import com.google.auto.service.AutoService;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.plugins.AbstractLineaOptionsPlugin;
import net.consensys.linea.plugins.BesuServiceProvider;
import net.consensys.linea.plugins.LineaOptionsPluginConfiguration;
import net.consensys.linea.plugins.rpc.RequestLimiter;
import net.consensys.linea.plugins.rpc.RequestLimiterDispatcher;
import org.hyperledger.besu.plugin.BesuContext;
import org.hyperledger.besu.plugin.BesuPlugin;
import org.hyperledger.besu.plugin.services.sync.SynchronizationService;

@Slf4j
@AutoService(BesuPlugin.class)
public class TracerReadinessPlugin extends AbstractLineaOptionsPlugin {
private static final String TRACER_READINESS_ENDPOINT_NAME = "/tracer-readiness";
private SynchronizationService synchronizationService;
private HttpServer server;
private BesuContext besuContext;

/**
* Register the needed Besu services.
*
* @param context the BesuContext to be used.
*/
@Override
public void register(final BesuContext context) {
super.register(context);
this.besuContext = context;
}

@Override
protected Map<String, LineaOptionsPluginConfiguration> getLineaPluginConfigMap() {
final TracerReadinessCliOptions tracerReadinessCliOptions = TracerReadinessCliOptions.create();

return Map.of(TracerReadinessCliOptions.CONFIG_KEY, tracerReadinessCliOptions.asPluginConfig());
}

@Override
public void start() {
// Initialize Vertx
final Vertx vertx = Vertx.vertx();

final Router router = Router.router(vertx);

// Register the readiness check handler
router
.get(TRACER_READINESS_ENDPOINT_NAME)
.handler(
routingContext -> {
if (isTracerReady()) {
routingContext.response().setStatusCode(200).end(statusResponse("UP"));
} else {
routingContext.response().setStatusCode(503).end(statusResponse("DOWN"));
}
});

TracerReadinessConfiguration configuration =
(TracerReadinessConfiguration)
getConfigurationByKey(TracerReadinessCliOptions.CONFIG_KEY).optionsConfig();

// Start the Vertx HTTP server
server =
vertx
.createHttpServer(httpServerOptions(configuration))
.requestHandler(router)
.listen(
configuration.serverPort(),
result -> {
final String pluginName = getClass().getSimpleName();
final int port = configuration.serverPort();

if (result.succeeded()) {
log.info("[{}] Started listening on port {}", pluginName, port);
} else {
log.error(
"[{}] Failed to start listening on port {}: {}",
pluginName,
port,
result.cause().getMessage());
}
});
}

private String statusResponse(final String status) {
return new JsonObject(singletonMap("status", status)).encodePrettily();
}

private HttpServerOptions httpServerOptions(final TracerReadinessConfiguration config) {
return new HttpServerOptions().setHost(config.serverHost()).setPort(config.serverPort());
}

private boolean isTracerReady() {
this.synchronizationService =
Optional.ofNullable(synchronizationService)
.orElse(BesuServiceProvider.getSynchronizationService(besuContext));

final RequestLimiter requestLimiter =
RequestLimiterDispatcher.getLimiter(
RequestLimiterDispatcher.SINGLE_INSTANCE_REQUEST_LIMITER_KEY);

return !requestLimiter.isNodeAtMaxCapacity() && synchronizationService.isInitialSyncPhaseDone();
}

@Override
public void stop() {
super.stop();
try {
server.close().toCompletionStage().toCompletableFuture().get();
} catch (Exception e) {
throw new RuntimeException("Error closing readiness endpoint HTTP server.", e);
}
}
}
Loading

0 comments on commit cf6cef9

Please sign in to comment.