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

feat: server properties configurable #365

Merged
merged 4 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions server/src/main/java/com/hedera/block/server/BlockNodeApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class BlockNodeApp {
private final WebServerConfig.Builder webServerBuilder;
private final PbjBlockStreamService pbjBlockStreamService;
private final PbjBlockAccessService pbjBlockAccessService;
private final ServerConfig serverConfig;

/**
* Constructs a new BlockNodeApp with the specified dependencies.
Expand All @@ -57,19 +58,22 @@ public class BlockNodeApp {
* @param pbjBlockStreamService defines the Block Stream services
* @param pbjBlockAccessService defines the Block Access services
* @param webServerBuilder used to build the web server and start it
* @param serverConfig has the server configuration
*/
@Inject
public BlockNodeApp(
@NonNull ServiceStatus serviceStatus,
@NonNull HealthService healthService,
@NonNull PbjBlockStreamService pbjBlockStreamService,
@NonNull PbjBlockAccessService pbjBlockAccessService,
@NonNull WebServerConfig.Builder webServerBuilder) {
@NonNull WebServerConfig.Builder webServerBuilder,
@NonNull ServerConfig serverConfig) {
this.serviceStatus = serviceStatus;
this.healthService = healthService;
this.pbjBlockStreamService = pbjBlockStreamService;
this.pbjBlockAccessService = pbjBlockAccessService;
this.webServerBuilder = webServerBuilder;
this.serverConfig = serverConfig;
}

/**
Expand All @@ -88,13 +92,13 @@ public void start() throws IOException {
// Override the default message size
final PbjConfig pbjConfig = PbjConfig.builder()
.name(PBJ_PROTOCOL_PROVIDER_CONFIG_NAME)
.maxMessageSizeBytes(1024 * 4096)
.maxMessageSizeBytes(serverConfig.maxMessageSizeBytes())
.build();

// Build the web server
// TODO: make port server a configurable value.
final WebServer webServer = webServerBuilder
.port(8080)
.port(serverConfig.port())
.addProtocol(pbjConfig)
.addRouting(pbjRouting)
.addRouting(httpRouting)
Expand Down
71 changes: 71 additions & 0 deletions server/src/main/java/com/hedera/block/server/ServerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.server;

import com.swirlds.config.api.ConfigData;
import com.swirlds.config.api.ConfigProperty;
import com.swirlds.config.api.validation.annotation.Max;
import com.swirlds.config.api.validation.annotation.Min;

/**
* Use this configuration across the server features
*
* <p>ServerConfig will have settings for the server.
*
* @param maxMessageSizeBytes the http2 max message/frame size in bytes
* @param port the port the server will listen on
*/
@ConfigData("server")
public record ServerConfig(
@ConfigProperty(defaultValue = "4_194_304") @Min(10_240) @Max(16_777_215) int maxMessageSizeBytes,
@ConfigProperty(defaultValue = "8080") @Min(1024) @Max(65_535) int port) {
AlfredoG87 marked this conversation as resolved.
Show resolved Hide resolved
private static final System.Logger LOGGER = System.getLogger(ServerConfig.class.getName());

/**
* Validate the configuration.
*
* @throws IllegalArgumentException if the configuration is invalid
*/
public ServerConfig {

validateMaxMessageSizeBytes(maxMessageSizeBytes);
validatePort(port);

LOGGER.log(System.Logger.Level.INFO, "Server configuration server.maxMessageSizeBytes: " + maxMessageSizeBytes);
LOGGER.log(System.Logger.Level.INFO, "Server configuration server.port: " + port);
}

private void validatePort(int port) {
if (port < 1024) {
throw new IllegalArgumentException("port must be greater than 1024");
}

if (port > 65_535) {
throw new IllegalArgumentException("port must be less than 65_535");
}
}

private void validateMaxMessageSizeBytes(int maxMessageSizeBytes) {
if (maxMessageSizeBytes < 10_240) {
throw new IllegalArgumentException("maxMessageSizeBytes must be greater than 10_240");
}

if (maxMessageSizeBytes > 16_777_215) {
throw new IllegalArgumentException("maxMessageSizeBytes must be less than 16_777_215");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.hedera.block.server.config;

import com.google.auto.service.AutoService;
import com.hedera.block.server.ServerConfig;
import com.hedera.block.server.consumer.ConsumerConfig;
import com.hedera.block.server.mediator.MediatorConfig;
import com.hedera.block.server.notifier.NotifierConfig;
Expand Down Expand Up @@ -54,6 +55,7 @@ public Set<Class<? extends Record>> getConfigDataTypes() {
PrometheusConfig.class,
ProducerConfig.class,
ConsumerConfig.class,
PersistenceStorageConfig.class);
PersistenceStorageConfig.class,
ServerConfig.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.hedera.block.server.config;

import com.hedera.block.server.ServerConfig;
import com.hedera.block.server.consumer.ConsumerConfig;
import com.hedera.block.server.mediator.MediatorConfig;
import com.hedera.block.server.notifier.NotifierConfig;
Expand Down Expand Up @@ -118,4 +119,16 @@ static NotifierConfig provideNotifierConfig(Configuration configuration) {
static ProducerConfig provideProducerConfig(Configuration configuration) {
return configuration.getConfigData(ProducerConfig.class);
}

/**
* Provides a server configuration singleton using the configuration.
*
* @param configuration is the configuration singleton
* @return a server configuration singleton
*/
@Singleton
@Provides
static ServerConfig provideServerConfig(Configuration configuration) {
return configuration.getConfigData(ServerConfig.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@
* A class that extends {@link MappedConfigSource} ir order to have project-relevant initialization.
*/
public final class ServerMappedConfigSourceInitializer {
private static final List<ConfigMapping> MAPPINGS =
List.of(
new ConfigMapping(
"consumer.timeoutThresholdMillis", "CONSUMER_TIMEOUT_THRESHOLD_MILLIS"),
new ConfigMapping(
"persistence.storage.rootPath", "PERSISTENCE_STORAGE_ROOT_PATH"),
new ConfigMapping("service.delayMillis", "SERVICE_DELAY_MILLIS"),
new ConfigMapping("mediator.ringBufferSize", "MEDIATOR_RING_BUFFER_SIZE"),
new ConfigMapping("notifier.ringBufferSize", "NOTIFIER_RING_BUFFER_SIZE"));
private static final List<ConfigMapping> MAPPINGS = List.of(
new ConfigMapping("consumer.timeoutThresholdMillis", "CONSUMER_TIMEOUT_THRESHOLD_MILLIS"),
new ConfigMapping("persistence.storage.rootPath", "PERSISTENCE_STORAGE_ROOT_PATH"),
new ConfigMapping("service.delayMillis", "SERVICE_DELAY_MILLIS"),
new ConfigMapping("mediator.ringBufferSize", "MEDIATOR_RING_BUFFER_SIZE"),
new ConfigMapping("notifier.ringBufferSize", "NOTIFIER_RING_BUFFER_SIZE"),
new ConfigMapping("server.maxMessageSizeBytes", "SERVER_MAX_MESSAGE_SIZE_BYTES"),
new ConfigMapping("server.port", "SERVER_PORT"));
AlfredoG87 marked this conversation as resolved.
Show resolved Hide resolved

private ServerMappedConfigSourceInitializer() {}

Expand All @@ -47,8 +46,7 @@ private ServerMappedConfigSourceInitializer() {}
*/
@NonNull
public static MappedConfigSource getMappedConfigSource() {
final MappedConfigSource config =
new MappedConfigSource(SystemEnvironmentConfigSource.getInstance());
final MappedConfigSource config = new MappedConfigSource(SystemEnvironmentConfigSource.getInstance());
MAPPINGS.forEach(config::addMapping);
return config;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,23 @@ class BlockNodeAppTest {
@Mock
private BlockNodeContext blockNodeContext;

ServerConfig serverConfig;

private BlockNodeApp blockNodeApp;

@BeforeEach
void setup() {

serverConfig = new ServerConfig(4_194_304, 8080);

blockNodeApp = new BlockNodeApp(
serviceStatus,
healthService,
new PbjBlockStreamServiceProxy(
liveStreamMediator, serviceStatus, blockNodeEventHandler, notifier, blockNodeContext),
new PbjBlockAccessServiceProxy(serviceStatus, blockReader, blockNodeContext),
webServerBuilder);
webServerBuilder,
serverConfig);

when(webServerBuilder.port(8080)).thenReturn(webServerBuilder);
when(webServerBuilder.addProtocol(any(PbjConfig.class))).thenReturn(webServerBuilder);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.server;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class ServerConfigTest {

@BeforeEach
void setUp() {}

@AfterEach
void tearDown() {}

@Test
void testValidValues() {

ServerConfig serverConfig = new ServerConfig(4_194_304, 8080);

assertEquals(4_194_304, serverConfig.maxMessageSizeBytes());
assertEquals(8080, serverConfig.port());
}

@Test
void testMessageSizeTooBig() {
assertThrows(IllegalArgumentException.class, () -> {
new ServerConfig(16_777_216, 8080);
});
}

@Test
void testMessageSizeTooSmall() {
assertThrows(IllegalArgumentException.class, () -> {
new ServerConfig(10_239, 8080);
});
}

@Test
void testPortValueTooBig() {
assertThrows(IllegalArgumentException.class, () -> {
new ServerConfig(4_194_304, 65_536);
});
}

@Test
void testPortValueTooSmall() {
assertThrows(IllegalArgumentException.class, () -> {
new ServerConfig(4_194_304, 1023);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static org.junit.jupiter.api.Assertions.*;

import com.hedera.block.server.ServerConfig;
import com.hedera.block.server.consumer.ConsumerConfig;
import com.hedera.block.server.mediator.MediatorConfig;
import com.hedera.block.server.notifier.NotifierConfig;
Expand All @@ -36,12 +37,10 @@ void testProvidePersistenceStorageConfig() throws IOException {

BlockNodeContext context = TestConfigUtil.getTestBlockNodeContext();
Configuration configuration = context.configuration();
PersistenceStorageConfig persistenceStorageConfig =
configuration.getConfigData(PersistenceStorageConfig.class);
PersistenceStorageConfig persistenceStorageConfig = configuration.getConfigData(PersistenceStorageConfig.class);

// Call the method under test
PersistenceStorageConfig providedConfig =
ConfigInjectionModule.providePersistenceStorageConfig(configuration);
PersistenceStorageConfig providedConfig = ConfigInjectionModule.providePersistenceStorageConfig(configuration);

// Verify that the correct config data is returned
assertNotNull(providedConfig);
Expand Down Expand Up @@ -71,8 +70,7 @@ void testProvidePrometheusConfig() throws IOException {
PrometheusConfig prometheusConfig = configuration.getConfigData(PrometheusConfig.class);

// Call the method under test
PrometheusConfig providedConfig =
ConfigInjectionModule.providePrometheusConfig(configuration);
PrometheusConfig providedConfig = ConfigInjectionModule.providePrometheusConfig(configuration);

// Verify that the correct config data is returned
assertNotNull(providedConfig);
Expand Down Expand Up @@ -119,4 +117,17 @@ void testProvideNotifierConfig() throws IOException {
assertNotNull(providedConfig);
assertSame(notifierConfig, providedConfig);
}

@Test
void testServerConfig() throws IOException {
BlockNodeContext context = TestConfigUtil.getTestBlockNodeContext();
Configuration configuration = context.configuration();
ServerConfig serverConfig = configuration.getConfigData(ServerConfig.class);

ServerConfig providedConfig = ConfigInjectionModule.provideServerConfig(configuration);

// Verify the config
assertNotNull(providedConfig);
assertSame(serverConfig, providedConfig);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ class ServerMappedConfigSourceInitializerTest {
new ConfigMapping("persistence.storage.rootPath", "PERSISTENCE_STORAGE_ROOT_PATH"),
new ConfigMapping("service.delayMillis", "SERVICE_DELAY_MILLIS"),
new ConfigMapping("mediator.ringBufferSize", "MEDIATOR_RING_BUFFER_SIZE"),
new ConfigMapping("notifier.ringBufferSize", "NOTIFIER_RING_BUFFER_SIZE")
new ConfigMapping("notifier.ringBufferSize", "NOTIFIER_RING_BUFFER_SIZE"),
new ConfigMapping("server.maxMessageSizeBytes", "SERVER_MAX_MESSAGE_SIZE_BYTES"),
new ConfigMapping("server.port", "SERVER_PORT"),
};
private static MappedConfigSource toTest;

Expand All @@ -59,19 +61,16 @@ void test_VerifyAllSupportedMappingsAreAddedToInstance() throws ReflectiveOperat

for (final ConfigMapping current : SUPPORTED_MAPPINGS) {
final Predicate<ConfigMapping> predicate =
cm ->
current.mappedName().equals(cm.mappedName())
&& current.originalName().equals(cm.originalName());
cm -> current.mappedName().equals(cm.mappedName())
&& current.originalName().equals(cm.originalName());
assertTrue(
actual.stream().anyMatch(predicate),
() ->
"when testing for: [%s] it is not contained in mappings of the actual initialized object %s"
.formatted(current, actual));
() -> "when testing for: [%s] it is not contained in mappings of the actual initialized object %s"
.formatted(current, actual));
}
}

private static Queue<ConfigMapping> extractConfigMappings()
throws ReflectiveOperationException {
private static Queue<ConfigMapping> extractConfigMappings() throws ReflectiveOperationException {
final Field configMappings = MappedConfigSource.class.getDeclaredField("configMappings");
try {
configMappings.setAccessible(true);
Expand Down
Loading