diff --git a/server/docker/docker-compose.yml b/server/docker/docker-compose.yml index aafcfd945..9c5023cb3 100644 --- a/server/docker/docker-compose.yml +++ b/server/docker/docker-compose.yml @@ -27,8 +27,11 @@ services: - ./metrics/prometheus.yml:/etc/prometheus/prometheus.yml command: - '--config.file=/etc/prometheus/prometheus.yml' + # Exposing Prometheus outside the stack can create issues with + # the MetricsService in the Simulator. Here it's exposed within + # the stack. ports: - - "9090:9090" + - "9090" grafana: image: grafana/grafana diff --git a/simulator/build.gradle.kts b/simulator/build.gradle.kts index e8012d4b1..18eaf43cc 100644 --- a/simulator/build.gradle.kts +++ b/simulator/build.gradle.kts @@ -41,7 +41,6 @@ testModuleInfo { requires("org.mockito") requires("org.mockito.junit.jupiter") requiresStatic("com.github.spotbugs.annotations") - requires("com.swirlds.common") } tasks.register("untarTestBlockStream") { diff --git a/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorApp.java b/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorApp.java index ebff3b538..5533736b4 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorApp.java +++ b/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorApp.java @@ -16,6 +16,7 @@ package com.hedera.block.simulator; +import static java.lang.System.Logger.Level.INFO; import static java.util.Objects.requireNonNull; import com.hedera.block.simulator.config.data.BlockStreamConfig; @@ -23,6 +24,7 @@ import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import com.hedera.block.simulator.generator.BlockStreamManager; import com.hedera.block.simulator.grpc.PublishStreamGrpcClient; +import com.hedera.block.simulator.metrics.MetricsService; import com.hedera.block.simulator.mode.CombinedModeHandler; import com.hedera.block.simulator.mode.ConsumerModeHandler; import com.hedera.block.simulator.mode.PublisherModeHandler; @@ -37,9 +39,11 @@ public class BlockStreamSimulatorApp { private final System.Logger LOGGER = System.getLogger(getClass().getName()); + private final PublishStreamGrpcClient publishStreamGrpcClient; private final SimulatorModeHandler simulatorModeHandler; private final AtomicBoolean isRunning = new AtomicBoolean(false); + private final MetricsService metricsService; /** * Creates a new BlockStreamSimulatorApp instance. @@ -47,14 +51,18 @@ public class BlockStreamSimulatorApp { * @param configuration the configuration to be used by the block stream simulator * @param blockStreamManager the block stream manager to be used by the block stream simulator * @param publishStreamGrpcClient the gRPC client to be used by the block stream simulator + * @param metricsService the metrics service to be used by the block stream simulator */ @Inject public BlockStreamSimulatorApp( @NonNull Configuration configuration, @NonNull BlockStreamManager blockStreamManager, - @NonNull PublishStreamGrpcClient publishStreamGrpcClient) { - requireNonNull(blockStreamManager); + @NonNull PublishStreamGrpcClient publishStreamGrpcClient, + @NonNull MetricsService metricsService) { + requireNonNull(configuration); + requireNonNull(blockStreamManager); + this.metricsService = requireNonNull(metricsService); this.publishStreamGrpcClient = requireNonNull(publishStreamGrpcClient); final BlockStreamConfig blockStreamConfig = requireNonNull(configuration.getConfigData(BlockStreamConfig.class)); @@ -97,6 +105,6 @@ public boolean isRunning() { public void stop() { publishStreamGrpcClient.shutdown(); isRunning.set(false); - LOGGER.log(System.Logger.Level.INFO, "Block Stream Simulator has stopped"); + LOGGER.log(INFO, "Block Stream Simulator has stopped"); } } diff --git a/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorInjectionComponent.java b/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorInjectionComponent.java index 93ca848dd..8e3d9cfef 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorInjectionComponent.java +++ b/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorInjectionComponent.java @@ -19,6 +19,7 @@ import com.hedera.block.simulator.config.ConfigInjectionModule; import com.hedera.block.simulator.generator.GeneratorInjectionModule; import com.hedera.block.simulator.grpc.GrpcInjectionModule; +import com.hedera.block.simulator.metrics.MetricsInjectionModule; import com.swirlds.config.api.Configuration; import dagger.BindsInstance; import dagger.Component; @@ -28,6 +29,7 @@ @Singleton @Component( modules = { + MetricsInjectionModule.class, ConfigInjectionModule.class, GeneratorInjectionModule.class, GrpcInjectionModule.class, diff --git a/simulator/src/main/java/com/hedera/block/simulator/config/ConfigInjectionModule.java b/simulator/src/main/java/com/hedera/block/simulator/config/ConfigInjectionModule.java index b752fa595..446d301ed 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/config/ConfigInjectionModule.java +++ b/simulator/src/main/java/com/hedera/block/simulator/config/ConfigInjectionModule.java @@ -19,6 +19,7 @@ import com.hedera.block.simulator.config.data.BlockGeneratorConfig; import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.data.GrpcConfig; +import com.swirlds.common.metrics.platform.prometheus.PrometheusConfig; import com.swirlds.config.api.Configuration; import dagger.Module; import dagger.Provides; @@ -63,4 +64,16 @@ static GrpcConfig provideGrpcConfig(Configuration configuration) { static BlockGeneratorConfig provideBlockGeneratorConfig(Configuration configuration) { return configuration.getConfigData(BlockGeneratorConfig.class); } + + /** + * Provides a Prometheus configuration singleton using the configuration. + * + * @param configuration is the configuration singleton + * @return a Prometheus configuration singleton + */ + @Singleton + @Provides + static PrometheusConfig providePrometheusConfig(Configuration configuration) { + return configuration.getConfigData(PrometheusConfig.class); + } } diff --git a/simulator/src/main/java/com/hedera/block/simulator/config/SimulatorConfigExtension.java b/simulator/src/main/java/com/hedera/block/simulator/config/SimulatorConfigExtension.java index b53f581b4..e26d80d11 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/config/SimulatorConfigExtension.java +++ b/simulator/src/main/java/com/hedera/block/simulator/config/SimulatorConfigExtension.java @@ -20,6 +20,8 @@ import com.hedera.block.simulator.config.data.BlockGeneratorConfig; import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.data.GrpcConfig; +import com.swirlds.common.metrics.config.MetricsConfig; +import com.swirlds.common.metrics.platform.prometheus.PrometheusConfig; import com.swirlds.config.api.ConfigurationExtension; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Set; @@ -36,6 +38,11 @@ public SimulatorConfigExtension() { @NonNull @Override public Set> getConfigDataTypes() { - return Set.of(BlockStreamConfig.class, GrpcConfig.class, BlockGeneratorConfig.class); + return Set.of( + BlockStreamConfig.class, + GrpcConfig.class, + BlockGeneratorConfig.class, + MetricsConfig.class, + PrometheusConfig.class); } } diff --git a/simulator/src/main/java/com/hedera/block/simulator/grpc/PublishStreamGrpcClientImpl.java b/simulator/src/main/java/com/hedera/block/simulator/grpc/PublishStreamGrpcClientImpl.java index 485d478a2..548db03c2 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/grpc/PublishStreamGrpcClientImpl.java +++ b/simulator/src/main/java/com/hedera/block/simulator/grpc/PublishStreamGrpcClientImpl.java @@ -16,12 +16,15 @@ package com.hedera.block.simulator.grpc; +import static com.hedera.block.simulator.metrics.SimulatorMetricTypes.Counter.LiveBlockItemsSent; +import static java.lang.System.Logger.Level.INFO; import static java.util.Objects.requireNonNull; import com.hedera.block.common.utils.ChunkUtils; import com.hedera.block.simulator.Translator; import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.data.GrpcConfig; +import com.hedera.block.simulator.metrics.MetricsService; import com.hedera.hapi.block.protoc.BlockStreamServiceGrpc; import com.hedera.hapi.block.protoc.PublishStreamRequest; import com.hedera.hapi.block.stream.Block; @@ -39,22 +42,29 @@ */ public class PublishStreamGrpcClientImpl implements PublishStreamGrpcClient { + private System.Logger LOGGER = System.getLogger(getClass().getName()); + private StreamObserver requestStreamObserver; private final BlockStreamConfig blockStreamConfig; private final GrpcConfig grpcConfig; private ManagedChannel channel; + private final MetricsService metricsService; /** * Creates a new PublishStreamGrpcClientImpl instance. * * @param grpcConfig the gRPC configuration * @param blockStreamConfig the block stream configuration + * @param metricsService the metrics service */ @Inject public PublishStreamGrpcClientImpl( - @NonNull final GrpcConfig grpcConfig, @NonNull final BlockStreamConfig blockStreamConfig) { + @NonNull final GrpcConfig grpcConfig, + @NonNull final BlockStreamConfig blockStreamConfig, + @NonNull final MetricsService metricsService) { this.grpcConfig = requireNonNull(grpcConfig); this.blockStreamConfig = requireNonNull(blockStreamConfig); + this.metricsService = requireNonNull(metricsService); } /** @@ -85,6 +95,11 @@ public boolean streamBlockItem(List blockItems) { requestStreamObserver.onNext(PublishStreamRequest.newBuilder() .addAllBlockItems(blockItemsProtoc) .build()); + metricsService.get(LiveBlockItemsSent).add(blockItemsProtoc.size()); + LOGGER.log( + INFO, + "Total Block items sent: {0}", + metricsService.get(LiveBlockItemsSent).get()); return true; } @@ -106,6 +121,11 @@ public boolean streamBlock(Block block) { requestStreamObserver.onNext(PublishStreamRequest.newBuilder() .addAllBlockItems(streamingBatch) .build()); + metricsService.get(LiveBlockItemsSent).add(streamingBatch.size()); + LOGGER.log( + INFO, + "Total Block items sent: {0}", + metricsService.get(LiveBlockItemsSent).get()); } return true; diff --git a/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsInjectionModule.java b/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsInjectionModule.java new file mode 100644 index 000000000..2751c04f3 --- /dev/null +++ b/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsInjectionModule.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * 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 com.hedera.block.simulator.metrics; + +import com.swirlds.common.metrics.platform.DefaultMetricsProvider; +import com.swirlds.config.api.Configuration; +import com.swirlds.metrics.api.Metrics; +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; + +/** The module used to inject the metrics service and metrics into the application. */ +@Module +public interface MetricsInjectionModule { + + /** + * Provides the metrics service. + * + * @param metricsService the metrics service to be used + * @return the metrics service + */ + @Singleton + @Binds + MetricsService bindMetricsService(MetricsServiceImpl metricsService); + + /** + * Provides the metrics. + * + * @param configuration the configuration to be used by the metrics + * @return the metrics + */ + @Singleton + @Provides + static Metrics provideMetrics(Configuration configuration) { + final DefaultMetricsProvider metricsProvider = new DefaultMetricsProvider(configuration); + final Metrics metrics = metricsProvider.createGlobalMetrics(); + metricsProvider.start(); + return metrics; + } +} diff --git a/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsService.java b/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsService.java new file mode 100644 index 000000000..959d42209 --- /dev/null +++ b/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsService.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * 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 com.hedera.block.simulator.metrics; + +import com.swirlds.metrics.api.Counter; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** Use member variables of this class to update metric data for the Hedera Block Node. */ +public interface MetricsService { + /** + * Use this method to get a specific counter for the given metric type. + * + * @param key to get a specific counter + * @return the counter + */ + Counter get(@NonNull SimulatorMetricTypes.Counter key); +} diff --git a/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsServiceImpl.java b/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsServiceImpl.java new file mode 100644 index 000000000..9b8b7ee61 --- /dev/null +++ b/simulator/src/main/java/com/hedera/block/simulator/metrics/MetricsServiceImpl.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * 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 com.hedera.block.simulator.metrics; + +import com.swirlds.metrics.api.Counter; +import com.swirlds.metrics.api.Metrics; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.EnumMap; +import javax.inject.Inject; + +/** + * Use member variables of this class to update metric data for the Hedera Block Node. + * + *

Metrics are updated by calling the appropriate method on the metric object instance. For + * example, to increment a counter, call {@link Counter#increment()}. + */ +public class MetricsServiceImpl implements MetricsService { + + private static final String CATEGORY = "hedera_block_node_simulator"; + + private final EnumMap counters = + new EnumMap<>(SimulatorMetricTypes.Counter.class); + + /** + * Create singleton instance of metrics service to be used throughout the application. + * + * @param metrics the metrics instance + */ + @Inject + public MetricsServiceImpl(@NonNull final Metrics metrics) { + // Initialize the counters + for (SimulatorMetricTypes.Counter counter : SimulatorMetricTypes.Counter.values()) { + counters.put( + counter, + metrics.getOrCreate(new Counter.Config(CATEGORY, counter.grafanaLabel()) + .withDescription(counter.description()))); + } + } + + /** + * Use this method to get a specific counter for the given metric type. + * + * @param key to get a specific counter + * @return the counter + */ + @NonNull + @Override + public Counter get(@NonNull SimulatorMetricTypes.Counter key) { + return counters.get(key); + } +} diff --git a/simulator/src/main/java/com/hedera/block/simulator/metrics/SimulatorMetricTypes.java b/simulator/src/main/java/com/hedera/block/simulator/metrics/SimulatorMetricTypes.java new file mode 100644 index 000000000..57e88a52c --- /dev/null +++ b/simulator/src/main/java/com/hedera/block/simulator/metrics/SimulatorMetricTypes.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * 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 com.hedera.block.simulator.metrics; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * The BlockNodeMetricNames class contains the names of the metrics used by the BlockNode. + * + *

These names are used to register the metrics with the metrics service. + */ +public final class SimulatorMetricTypes { + private SimulatorMetricTypes() {} + + /** + * Add new counting metrics to this enum to automatically register them with the metrics + * service. + * + *

Each enum value should have a unique grafana label and meaningful description. These + * counters can capture data on standard operations or errors. + */ + public enum Counter implements MetricMetadata { + // Standard counters + /** The number of live block items sent by the simulator . */ + LiveBlockItemsSent("live_block_items_sent", "Live Block Items Sent"); + + private final String grafanaLabel; + private final String description; + + Counter(String grafanaLabel, String description) { + this.grafanaLabel = grafanaLabel; + this.description = description; + } + + @Override + @NonNull + public String grafanaLabel() { + return grafanaLabel; + } + + @Override + @NonNull + public String description() { + return description; + } + } + + private interface MetricMetadata { + String grafanaLabel(); + + String description(); + } +} diff --git a/simulator/src/main/java/module-info.java b/simulator/src/main/java/module-info.java index 0b694464b..fb2ab01d5 100644 --- a/simulator/src/main/java/module-info.java +++ b/simulator/src/main/java/module-info.java @@ -5,6 +5,11 @@ exports com.hedera.block.simulator.config.data; exports com.hedera.block.simulator.exception; exports com.hedera.block.simulator; + exports com.hedera.block.simulator.config.types; + exports com.hedera.block.simulator.config; + exports com.hedera.block.simulator.grpc; + exports com.hedera.block.simulator.generator; + exports com.hedera.block.simulator.metrics; requires static com.github.spotbugs.annotations; requires static com.google.auto.service; @@ -12,8 +17,10 @@ requires com.hedera.block.stream; requires com.google.protobuf; requires com.hedera.pbj.runtime; + requires com.swirlds.common; requires com.swirlds.config.api; requires com.swirlds.config.extensions; + requires com.swirlds.metrics.api; requires dagger; requires io.grpc.stub; requires io.grpc; diff --git a/simulator/src/main/resources/app.properties b/simulator/src/main/resources/app.properties index 2999dc112..3539c8e5c 100644 --- a/simulator/src/main/resources/app.properties +++ b/simulator/src/main/resources/app.properties @@ -1,3 +1,12 @@ +# The prometheus endpoint is used by the +# MetricsService to track different metrics. +# This is disabled by default to avoid port +# conflicts with the server prometheus endpoint. +# We might consider enabling this on a different +# port to track simulator metrics from a +# dashboard. +prometheus.endpointEnabled=false + #generator.folderRootPath=/Users/user/Downloads/block-0.0.3-perf #generator.managerImplementation=BlockAsFileLargeDataSets #blockStream.maxBlockItemsToStream=100_000_000 diff --git a/simulator/src/test/java/com/hedera/block/simulator/BlockStreamSimulatorTest.java b/simulator/src/test/java/com/hedera/block/simulator/BlockStreamSimulatorTest.java index 72b8a40a6..51b32f72c 100644 --- a/simulator/src/test/java/com/hedera/block/simulator/BlockStreamSimulatorTest.java +++ b/simulator/src/test/java/com/hedera/block/simulator/BlockStreamSimulatorTest.java @@ -16,6 +16,7 @@ package com.hedera.block.simulator; +import static com.hedera.block.simulator.TestUtils.getTestMetrics; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -28,6 +29,8 @@ import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import com.hedera.block.simulator.generator.BlockStreamManager; import com.hedera.block.simulator.grpc.PublishStreamGrpcClient; +import com.hedera.block.simulator.metrics.MetricsService; +import com.hedera.block.simulator.metrics.MetricsServiceImpl; import com.hedera.block.simulator.mode.PublisherModeHandler; import com.hedera.hapi.block.stream.Block; import com.hedera.hapi.block.stream.BlockItem; @@ -58,6 +61,7 @@ class BlockStreamSimulatorTest { private PublishStreamGrpcClient publishStreamGrpcClient; private BlockStreamSimulatorApp blockStreamSimulator; + private MetricsService metricsService; @BeforeEach void setUp() throws IOException { @@ -65,7 +69,11 @@ void setUp() throws IOException { Configuration configuration = TestUtils.getTestConfiguration( Map.of("blockStream.maxBlockItemsToStream", "100", "blockStream.streamingMode", "CONSTANT_RATE")); - blockStreamSimulator = new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient); + Configuration config = TestUtils.getTestConfiguration(); + metricsService = new MetricsServiceImpl(getTestMetrics(config)); + + blockStreamSimulator = + new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient, metricsService); } @AfterEach @@ -105,7 +113,7 @@ void start_constantRateStreaming() throws InterruptedException, BlockSimulatorPa "2")); BlockStreamSimulatorApp blockStreamSimulator = - new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient); + new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient, metricsService); blockStreamSimulator.start(); assertTrue(blockStreamSimulator.isRunning()); @@ -141,7 +149,7 @@ void start_millisPerBlockStreaming() throws InterruptedException, IOException, B "MILLIS_PER_BLOCK")); BlockStreamSimulatorApp blockStreamSimulator = - new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient); + new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient, metricsService); blockStreamSimulator.start(); assertTrue(blockStreamSimulator.isRunning()); @@ -184,7 +192,7 @@ void start_millisPerSecond_streamingLagVerifyWarnLog() "1")); BlockStreamSimulatorApp blockStreamSimulator = - new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient); + new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient, metricsService); blockStreamSimulator.start(); assertTrue(blockStreamSimulator.isRunning()); @@ -198,14 +206,16 @@ void start_millisPerSecond_streamingLagVerifyWarnLog() @Test void start_withBothMode_throwsUnsupportedOperationException() throws Exception { Configuration configuration = TestUtils.getTestConfiguration(Map.of("blockStream.simulatorMode", "BOTH")); - blockStreamSimulator = new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient); + blockStreamSimulator = + new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient, metricsService); assertThrows(UnsupportedOperationException.class, () -> blockStreamSimulator.start()); } @Test void start_withConsumerMode_throwsUnsupportedOperationException() throws Exception { Configuration configuration = TestUtils.getTestConfiguration(Map.of("blockStream.simulatorMode", "CONSUMER")); - blockStreamSimulator = new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient); + blockStreamSimulator = + new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient, metricsService); assertThrows(UnsupportedOperationException.class, () -> blockStreamSimulator.start()); } @@ -218,7 +228,7 @@ void constructor_throwsExceptionForNullSimulatorMode() { when(blockStreamConfig.simulatorMode()).thenReturn(null); assertThrows(NullPointerException.class, () -> { - new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient); + new BlockStreamSimulatorApp(configuration, blockStreamManager, publishStreamGrpcClient, metricsService); }); } diff --git a/simulator/src/test/java/com/hedera/block/simulator/TestUtils.java b/simulator/src/test/java/com/hedera/block/simulator/TestUtils.java index dc093abda..2b5865ec2 100644 --- a/simulator/src/test/java/com/hedera/block/simulator/TestUtils.java +++ b/simulator/src/test/java/com/hedera/block/simulator/TestUtils.java @@ -17,9 +17,11 @@ package com.hedera.block.simulator; import com.hedera.block.simulator.config.TestConfigBuilder; +import com.swirlds.common.metrics.platform.DefaultMetricsProvider; import com.swirlds.config.api.Configuration; import com.swirlds.config.extensions.sources.ClasspathFileConfigSource; import com.swirlds.config.extensions.sources.SimpleConfigSource; +import com.swirlds.metrics.api.Metrics; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.nio.file.Path; @@ -29,20 +31,15 @@ public class TestUtils { private static final String TEST_APP_PROPERTIES_FILE = "app.properties"; - public static Configuration getTestConfiguration(@NonNull Map customProperties) - throws IOException { + public static Configuration getTestConfiguration(@NonNull Map customProperties) throws IOException { // create test configuration - TestConfigBuilder testConfigBuilder = - new TestConfigBuilder(true) - .withSource( - new ClasspathFileConfigSource(Path.of(TEST_APP_PROPERTIES_FILE))); + TestConfigBuilder testConfigBuilder = new TestConfigBuilder(true) + .withSource(new ClasspathFileConfigSource(Path.of(TEST_APP_PROPERTIES_FILE))); for (Map.Entry entry : customProperties.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); - testConfigBuilder = - testConfigBuilder.withSource( - new SimpleConfigSource(key, value).withOrdinal(500)); + testConfigBuilder = testConfigBuilder.withSource(new SimpleConfigSource(key, value).withOrdinal(500)); } return testConfigBuilder.getOrCreateConfig(); @@ -51,4 +48,11 @@ public static Configuration getTestConfiguration(@NonNull Map cu public static Configuration getTestConfiguration() throws IOException { return getTestConfiguration(Map.of()); } + + public static Metrics getTestMetrics(@NonNull Configuration configuration) { + final DefaultMetricsProvider metricsProvider = new DefaultMetricsProvider(configuration); + final Metrics metrics = metricsProvider.createGlobalMetrics(); + metricsProvider.start(); + return metrics; + } } diff --git a/simulator/src/test/java/com/hedera/block/simulator/config/ConfigInjectionModuleTest.java b/simulator/src/test/java/com/hedera/block/simulator/config/ConfigInjectionModuleTest.java index c499c5076..1afed50ec 100644 --- a/simulator/src/test/java/com/hedera/block/simulator/config/ConfigInjectionModuleTest.java +++ b/simulator/src/test/java/com/hedera/block/simulator/config/ConfigInjectionModuleTest.java @@ -16,17 +16,21 @@ package com.hedera.block.simulator.config; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + import com.hedera.block.simulator.TestUtils; import com.hedera.block.simulator.config.data.BlockGeneratorConfig; import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.data.GrpcConfig; +import com.swirlds.common.metrics.platform.prometheus.PrometheusConfig; import com.swirlds.config.api.Configuration; import com.swirlds.config.api.ConfigurationBuilder; import com.swirlds.config.extensions.sources.ClasspathFileConfigSource; import java.io.IOException; import java.nio.file.Path; import java.util.Map; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -36,42 +40,46 @@ class ConfigInjectionModuleTest { @BeforeAll static void setUpAll() throws IOException { - configuration = - ConfigurationBuilder.create() - .withSource(new ClasspathFileConfigSource(Path.of("app.properties"))) - .autoDiscoverExtensions() - .build(); - configuration = - TestUtils.getTestConfiguration( - Map.of("generator.managerImplementation", "BlockAsFileBlockStreamManager")); + configuration = ConfigurationBuilder.create() + .withSource(new ClasspathFileConfigSource(Path.of("app.properties"))) + .autoDiscoverExtensions() + .build(); + configuration = TestUtils.getTestConfiguration( + Map.of("generator.managerImplementation", "BlockAsFileBlockStreamManager")); } @Test void provideBlockStreamConfig() { - BlockStreamConfig blockStreamConfig = - ConfigInjectionModule.provideBlockStreamConfig(configuration); + BlockStreamConfig blockStreamConfig = ConfigInjectionModule.provideBlockStreamConfig(configuration); - Assertions.assertNotNull(blockStreamConfig); - Assertions.assertEquals(1000, blockStreamConfig.blockItemsBatchSize()); + assertNotNull(blockStreamConfig); + assertEquals(1000, blockStreamConfig.blockItemsBatchSize()); } @Test void provideGrpcConfig() { GrpcConfig grpcConfig = ConfigInjectionModule.provideGrpcConfig(configuration); - Assertions.assertNotNull(grpcConfig); - Assertions.assertEquals("localhost", grpcConfig.serverAddress()); - Assertions.assertEquals(8080, grpcConfig.port()); + assertNotNull(grpcConfig); + assertEquals("localhost", grpcConfig.serverAddress()); + assertEquals(8080, grpcConfig.port()); } @Test void provideBlockGeneratorConfig() { - BlockGeneratorConfig blockGeneratorConfig = - ConfigInjectionModule.provideBlockGeneratorConfig(configuration); + BlockGeneratorConfig blockGeneratorConfig = ConfigInjectionModule.provideBlockGeneratorConfig(configuration); + + assertNotNull(blockGeneratorConfig); + assertEquals("BlockAsFileBlockStreamManager", blockGeneratorConfig.managerImplementation()); + } + + @Test + void providePrometheusConfig() { + PrometheusConfig prometheusConfig = ConfigInjectionModule.providePrometheusConfig(configuration); - Assertions.assertNotNull(blockGeneratorConfig); - Assertions.assertEquals( - "BlockAsFileBlockStreamManager", blockGeneratorConfig.managerImplementation()); + assertNotNull(prometheusConfig); + assertFalse(prometheusConfig.endpointEnabled()); + assertEquals(9999, prometheusConfig.endpointPortNumber()); } } diff --git a/simulator/src/test/java/com/hedera/block/simulator/grpc/PublishStreamGrpcClientImplTest.java b/simulator/src/test/java/com/hedera/block/simulator/grpc/PublishStreamGrpcClientImplTest.java index 486bf8782..b768f63b6 100644 --- a/simulator/src/test/java/com/hedera/block/simulator/grpc/PublishStreamGrpcClientImplTest.java +++ b/simulator/src/test/java/com/hedera/block/simulator/grpc/PublishStreamGrpcClientImplTest.java @@ -16,6 +16,7 @@ package com.hedera.block.simulator.grpc; +import static com.hedera.block.simulator.TestUtils.getTestMetrics; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -23,8 +24,11 @@ import com.hedera.block.simulator.TestUtils; import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.data.GrpcConfig; +import com.hedera.block.simulator.metrics.MetricsService; +import com.hedera.block.simulator.metrics.MetricsServiceImpl; import com.hedera.hapi.block.stream.Block; import com.hedera.hapi.block.stream.BlockItem; +import com.swirlds.config.api.Configuration; import io.grpc.ManagedChannel; import java.io.IOException; import java.lang.reflect.Field; @@ -33,11 +37,15 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(MockitoExtension.class) class PublishStreamGrpcClientImplTest { GrpcConfig grpcConfig; BlockStreamConfig blockStreamConfig; + MetricsService metricsService; @BeforeEach void setUp() throws IOException { @@ -45,6 +53,9 @@ void setUp() throws IOException { grpcConfig = TestUtils.getTestConfiguration().getConfigData(GrpcConfig.class); blockStreamConfig = TestUtils.getTestConfiguration(Map.of("blockStream.blockItemsBatchSize", "2")) .getConfigData(BlockStreamConfig.class); + + Configuration config = TestUtils.getTestConfiguration(); + metricsService = new MetricsServiceImpl(getTestMetrics(config)); } @AfterEach @@ -54,7 +65,7 @@ void tearDown() {} void streamBlockItem() { BlockItem blockItem = BlockItem.newBuilder().build(); PublishStreamGrpcClientImpl publishStreamGrpcClient = - new PublishStreamGrpcClientImpl(grpcConfig, blockStreamConfig); + new PublishStreamGrpcClientImpl(grpcConfig, blockStreamConfig, metricsService); publishStreamGrpcClient.init(); boolean result = publishStreamGrpcClient.streamBlockItem(List.of(blockItem)); assertTrue(result); @@ -68,7 +79,7 @@ void streamBlock() { Block block1 = Block.newBuilder().items(blockItem, blockItem, blockItem).build(); PublishStreamGrpcClientImpl publishStreamGrpcClient = - new PublishStreamGrpcClientImpl(grpcConfig, blockStreamConfig); + new PublishStreamGrpcClientImpl(grpcConfig, blockStreamConfig, metricsService); publishStreamGrpcClient.init(); boolean result = publishStreamGrpcClient.streamBlock(block); assertTrue(result); @@ -80,7 +91,7 @@ void streamBlock() { @Test void testShutdown() throws Exception { PublishStreamGrpcClientImpl publishStreamGrpcClient = - new PublishStreamGrpcClientImpl(grpcConfig, blockStreamConfig); + new PublishStreamGrpcClientImpl(grpcConfig, blockStreamConfig, metricsService); publishStreamGrpcClient.init(); Field channelField = PublishStreamGrpcClientImpl.class.getDeclaredField("channel"); diff --git a/simulator/src/test/java/com/hedera/block/simulator/metrics/MetricsInjectionModuleTest.java b/simulator/src/test/java/com/hedera/block/simulator/metrics/MetricsInjectionModuleTest.java new file mode 100644 index 000000000..ac3a099cf --- /dev/null +++ b/simulator/src/test/java/com/hedera/block/simulator/metrics/MetricsInjectionModuleTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * 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 com.hedera.block.simulator.metrics; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.hedera.block.simulator.TestUtils; +import com.swirlds.config.api.Configuration; +import com.swirlds.metrics.api.Metrics; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +public class MetricsInjectionModuleTest { + + @Test + void testProvideMetrics() throws IOException { + Configuration configuration = TestUtils.getTestConfiguration(); + + // Call the method under test + Metrics providedMetrics = MetricsInjectionModule.provideMetrics(configuration); + + assertNotNull(providedMetrics); + } +} diff --git a/simulator/src/test/java/com/hedera/block/simulator/metrics/MetricsServiceTest.java b/simulator/src/test/java/com/hedera/block/simulator/metrics/MetricsServiceTest.java new file mode 100644 index 000000000..e77451465 --- /dev/null +++ b/simulator/src/test/java/com/hedera/block/simulator/metrics/MetricsServiceTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * 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 com.hedera.block.simulator.metrics; + +import static com.hedera.block.simulator.TestUtils.getTestMetrics; +import static com.hedera.block.simulator.metrics.SimulatorMetricTypes.Counter.LiveBlockItemsSent; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.hedera.block.simulator.TestUtils; +import com.swirlds.config.api.Configuration; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class MetricsServiceTest { + + private MetricsService metricsService; + + @BeforeEach + public void setUp() throws IOException { + Configuration config = TestUtils.getTestConfiguration(); + metricsService = new MetricsServiceImpl(getTestMetrics(config)); + } + + @Test + void MetricsService_verifyLiveBlockItemsSentCounter() { + + for (int i = 0; i < 10; i++) { + metricsService.get(LiveBlockItemsSent).increment(); + } + + assertEquals( + LiveBlockItemsSent.grafanaLabel(), + metricsService.get(LiveBlockItemsSent).getName()); + assertEquals( + LiveBlockItemsSent.description(), + metricsService.get(LiveBlockItemsSent).getDescription()); + assertEquals(10, metricsService.get(LiveBlockItemsSent).get()); + } +}