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 all 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
16 changes: 9 additions & 7 deletions server/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ The default configuration allows users to quickly get up and running without hav
ease of use at the trade-off of some insecure default configuration. Most configuration settings have appropriate
defaults and can be left unchanged. It is recommended to browse the properties below and adjust to your needs.

| Environment Variable | Description | Default Value |
|:---|:---|---:|
| PERSISTENCE_STORAGE_ROOT_PATH | The root path for the storage, if not provided will attempt to create a `data` on the working dir of the app. | |
| CONSUMER_TIMEOUT_THRESHOLD_MILLIS | Time to wait for subscribers before disconnecting in milliseconds | 1500 |
| SERVICE_DELAY_MILLIS | Service shutdown delay in milliseconds | 500 |
| MEDIATOR_RING_BUFFER_SIZE | Size of the ring buffer used by the mediator (must be a power of 2) | 67108864 |
| NOTIFIER_RING_BUFFER_SIZE | Size of the ring buffer used by the notifier (must be a power of 2) | 2048 |
| Environment Variable | Description | Default Value |
|:----------------------------------|:--------------------------------------------------------------------------------------------------------------|:--------------|
| PERSISTENCE_STORAGE_ROOT_PATH | The root path for the storage, if not provided will attempt to create a `data` on the working dir of the app. | |
| CONSUMER_TIMEOUT_THRESHOLD_MILLIS | Time to wait for subscribers before disconnecting in milliseconds | 1500 |
| SERVICE_DELAY_MILLIS | Service shutdown delay in milliseconds | 500 |
| MEDIATOR_RING_BUFFER_SIZE | Size of the ring buffer used by the mediator (must be a power of 2) | 67108864 |
| NOTIFIER_RING_BUFFER_SIZE | Size of the ring buffer used by the notifier (must be a power of 2) | 2048 |
| SERVER_PORT | The port the server will listen on | 8080 |
| SERVER_MAX_MESSAGE_SIZE_BYTES | The maximum size of a message frame in bytes | 1048576 |
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
84 changes: 84 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,84 @@
/*
* 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 = defaultMaxMessageSizeBytes)
@Min(minMaxMessageSizeBytes)
@Max(maxMaxMessageSizeBytes)
int maxMessageSizeBytes,
@ConfigProperty(defaultValue = defaultPort) @Min(minPort) @Max(maxPort) int port) {
private static final System.Logger LOGGER = System.getLogger(ServerConfig.class.getName());

// Constants for maxMessageSizeBytes property
private static final String defaultMaxMessageSizeBytes = "4_194_304";
private static final long minMaxMessageSizeBytes = 10_240;
private static final long maxMaxMessageSizeBytes = 16_777_215;

// Constants for port property
private static final String defaultPort = "8080";
private static final int minPort = 1024;
private static final int maxPort = 65_535;

/**
* 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 < minPort) {
throw new IllegalArgumentException("port must be greater than " + minPort);
}

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

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

if (maxMessageSizeBytes > maxMaxMessageSizeBytes) {
throw new IllegalArgumentException("maxMessageSizeBytes must be less than " + maxMaxMessageSizeBytes);
}
}
}
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);
}
}
Loading
Loading