Skip to content

Commit

Permalink
refactoring configuration management
Browse files Browse the repository at this point in the history
Signed-off-by: Atanas Atanasov <[email protected]>
  • Loading branch information
ata-nas committed Oct 17, 2024
1 parent 611e91b commit 4f39560
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 36 deletions.
21 changes: 7 additions & 14 deletions .github/workflows/smoke-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ jobs:
sudo tar -xzf grpcurl.tar.gz -C /usr/local/bin grpcurl
rm grpcurl.tar.gz
- name: Cache Gradle packages
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
Expand All @@ -76,18 +75,12 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Build application
run: ${{ env.GRADLE_EXEC }} build

- name: Run application in background, capture logs in app.log
run: |
${{ env.GRADLE_EXEC }} :server:run 2> server/src/test/resources/app.log < /dev/null &
echo "Application started with PID $APP_PID"
sleep 10
- name: Build And Run Smoke Tests Container
run: ${{ env.GRADLE_EXEC }} buildAndRunSmokeTestsContainer -x test

- name: Print App Logs
run: cat server/src/test/resources/app.log

- name: Smoke Test
- name: Run Smoke Test
working-directory: server/src/test/resources/
run: ./smoke-test.sh app.log
run: ./smoke-test.sh

- name: Stop Smoke Tests Container
run: docker compose -p block-node stop
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ Please do not file a public ticket mentioning the vulnerability. Refer to the se

## Configuration

| Environment Variable | Description | Default Value |
|---------------------------------|---------------------------------------------------------------------------------------------------------------|---------------|
| persistence.storage.rootPath | The root path for the storage, if not provided will attempt to create a `data` on the working dir of the app. | ./data |
| consumer.timeoutThresholdMillis | Time to wait for subscribers before disconnecting in milliseconds | 1500 |


| 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 |

# Starting locally:
```bash
Expand Down
29 changes: 27 additions & 2 deletions server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ var updateDockerEnv =
group = "docker"

workingDir(layout.projectDirectory.dir("docker"))
commandLine("./update-env.sh", project.version)
commandLine("sh", "-c", "./update-env.sh ${project.version} false false")
}

tasks.register<Exec>("createDockerImage") {
Expand Down Expand Up @@ -101,7 +101,7 @@ tasks.register<Exec>("startDockerDebugContainer") {
commandLine(
"sh",
"-c",
"./update-env.sh ${project.version} true && docker compose -p block-node up -d"
"./update-env.sh ${project.version} true false && docker compose -p block-node up -d"
)
}

Expand All @@ -113,3 +113,28 @@ tasks.register<Exec>("stopDockerContainer") {
workingDir(layout.projectDirectory.dir("docker"))
commandLine("sh", "-c", "docker-compose -p block-node stop")
}

tasks.register("buildAndRunSmokeTestsContainer") {
doFirst {
// ensure smoke test .env properties before creating the container
exec {
workingDir(layout.projectDirectory.dir("docker"))
commandLine("sh", "-c", "./update-env.sh ${project.version} false true")
}
}

// build the project
dependsOn(tasks.build)

doLast {
// build and start smoke test container
exec {
workingDir(layout.projectDirectory.dir("docker"))
commandLine(
"sh",
"-c",
"./docker-build.sh ${project.version} && docker compose -p block-node up -d"
)
}
}
}
24 changes: 19 additions & 5 deletions server/docker/update-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,33 @@
# This script is called by gradle and get the current project version as an input param

if [ $# -lt 1 ]; then
echo "USAGE: $0 <VERSION> <DEBUG>"
echo "USAGE: $0 <VERSION> <DEBUG> <SMOKE_TEST>"
exit 1
fi

echo "VERSION=$1" > .env
project_version=$1
# determine if we should include debug opts
[ "$2" = true ] && is_debug=true || is_debug=false
# determine if we should include smoke test env variables
[ "$3" = true ] && is_smoke_test=true || is_smoke_test=false

echo "VERSION=$project_version" > .env
echo "REGISTRY_PREFIX=" >> .env
# Storage root path, this is temporary until we have a proper .properties file for all configs
echo "BLOCKNODE_STORAGE_ROOT_PATH=/app/storage" >> .env
echo "JAVA_OPTS='-Xms8G -Xmx16G'" >> .env

if [ $# -eq 2 ]; then
if [ true = "$is_debug" ]; then
# wait for debugger to attach
echo "SERVER_OPTS='-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005'" >> .env
fi

echo "DEBUG $2"
echo "VERSION/TAG UPDATED TO $1"
if [ true = "$is_smoke_test" ]; then
# add smoke test variables
echo "MEDIATOR_RING_BUFFER_SIZE=1024" >> .env
echo "NOTIFIER_RING_BUFFER_SIZE=1024" >> .env
fi

# Output the values
echo ".env properties:"
cat .env
4 changes: 2 additions & 2 deletions server/src/main/java/com/hedera/block/server/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
import static java.lang.System.Logger;
import static java.lang.System.Logger.Level.INFO;

import com.hedera.block.server.config.ServerMappedConfigSourceInitializer;
import com.swirlds.config.api.Configuration;
import com.swirlds.config.api.ConfigurationBuilder;
import com.swirlds.config.extensions.sources.ClasspathFileConfigSource;
import com.swirlds.config.extensions.sources.SystemEnvironmentConfigSource;
import com.swirlds.config.extensions.sources.SystemPropertiesConfigSource;
import io.helidon.config.Config;
import java.io.IOException;
Expand Down Expand Up @@ -59,7 +59,7 @@ public static void main(final String[] args) throws IOException {
// Init BlockNode Configuration
final Configuration configuration =
ConfigurationBuilder.create()
.withSource(SystemEnvironmentConfigSource.getInstance())
.withSource(ServerMappedConfigSourceInitializer.getMappedConfigSource())
.withSource(SystemPropertiesConfigSource.getInstance())
.withSources(new ClasspathFileConfigSource(Path.of(APPLICATION_PROPERTIES)))
.autoDiscoverExtensions()
Expand Down
Original file line number Diff line number Diff line change
@@ -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.server.config;

import com.swirlds.config.extensions.sources.ConfigMapping;
import com.swirlds.config.extensions.sources.MappedConfigSource;
import com.swirlds.config.extensions.sources.SystemEnvironmentConfigSource;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.List;

/**
* 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 ServerMappedConfigSourceInitializer() {}

/**
* This method constructs, initializes and returns a new instance of {@link MappedConfigSource}
* which internally uses {@link SystemEnvironmentConfigSource} and maps relevant config keys to
* other keys so that they could be used within the application
*
* @return newly constructed fully initialized {@link MappedConfigSource}
*/
@NonNull
public static MappedConfigSource getMappedConfigSource() {
final MappedConfigSource config =
new MappedConfigSource(SystemEnvironmentConfigSource.getInstance());
MAPPINGS.forEach(config::addMapping);
return config;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@
*
* @param ringBufferSize the size of the ring buffer used by the mediator
*/
// TODO: defaultValue here should be 67108864
@ConfigData("mediator")
public record MediatorConfig(@ConfigProperty(defaultValue = "1024") int ringBufferSize) {
public record MediatorConfig(@ConfigProperty(defaultValue = "67108864") int ringBufferSize) {
private static final System.Logger LOGGER = System.getLogger(MediatorConfig.class.getName());

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@
*
* @param ringBufferSize the size of the ring buffer used by the notifier
*/
// TODO: defaultValue here should be 2048
@ConfigData("notifier")
public record NotifierConfig(@ConfigProperty(defaultValue = "1024") int ringBufferSize) {
public record NotifierConfig(@ConfigProperty(defaultValue = "2048") int ringBufferSize) {
private static final System.Logger LOGGER = System.getLogger(NotifierConfig.class.getName());

/**
Expand Down
4 changes: 2 additions & 2 deletions server/src/main/resources/app.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
prometheus.endpointPortNumber=9999

# Ring buffer sizes for the mediator and notifier
mediator.ringBufferSize=67108864
notifier.ringBufferSize=2048
#mediator.ringBufferSize=
#notifier.ringBufferSize=

# Timeout for consumers to wait for block item before timing out. Default is 1500 milliseconds.
#consumer.timeoutThresholdMillis=1500
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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.config;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.swirlds.config.extensions.sources.ConfigMapping;
import com.swirlds.config.extensions.sources.MappedConfigSource;
import java.lang.reflect.Field;
import java.util.Queue;
import java.util.function.Predicate;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

class ServerMappedConfigSourceInitializerTest {
private static final ConfigMapping[] SUPPORTED_MAPPINGS = {
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 MappedConfigSource toTest;

@BeforeAll
static void setUp() {
toTest = ServerMappedConfigSourceInitializer.getMappedConfigSource();
}

/**
* This test aims to fail if we have added or removed any {@link ConfigMapping} that will be
* initialized by the {@link ServerMappedConfigSourceInitializer#getMappedConfigSource()}
* without reflecting it here in the test. The purpose is to bring attention to any changes to
* the developer so we can make sure we are aware of them in order to be sure we require the
* change. This test is more to bring attention than to test actual logic. So if this fails, we
* either change the {@link #SUPPORTED_MAPPINGS} here or the {@link
* ServerMappedConfigSourceInitializer#MAPPINGS} to make this pass.
*/
@Test
void test_VerifyAllSupportedMappingsAreAddedToInstance() throws ReflectiveOperationException {
final Queue<ConfigMapping> actual = extractConfigMappings();

assertEquals(SUPPORTED_MAPPINGS.length, actual.size());

for (final ConfigMapping current : SUPPORTED_MAPPINGS) {
final Predicate<ConfigMapping> predicate =
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));
}
}

private static Queue<ConfigMapping> extractConfigMappings()
throws ReflectiveOperationException {
final Field configMappings = MappedConfigSource.class.getDeclaredField("configMappings");
try {
configMappings.setAccessible(true);
return (Queue<ConfigMapping>) configMappings.get(toTest);
} finally {
configMappings.setAccessible(false);
}
}
}
2 changes: 1 addition & 1 deletion suites/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ val updateDockerEnv =
group = "docker"

workingDir(layout.projectDirectory.dir("../server/docker"))
commandLine("./update-env.sh", project.version)
commandLine("sh", "-c", "./update-env.sh ${project.version} false false")
}

// Task to build the Docker image
Expand Down

0 comments on commit 4f39560

Please sign in to comment.