Skip to content

Commit 207993b

Browse files
authored
Adding network latency simulator for block live stream consumers (#34)
Signed-off-by: Alfredo Gutierrez <[email protected]>
1 parent 8e98b5b commit 207993b

File tree

10 files changed

+182
-9
lines changed

10 files changed

+182
-9
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,16 @@ Please do not file a public ticket mentioning the vulnerability. Refer to the se
3333
# Running Locally
3434

3535
1) Create a local temp directory. For example, use `mktemp -d -t block-stream-temp-dir` to create a directory
36-
2) export BLOCKNODE_STORAGE_ROOT_PATH=<path to the temp directory> # You can add this to your .zshrc, etc
36+
2) Configuration variables
37+
```
38+
export BLOCKNODE_STORAGE_ROOT_PATH=<path to the temp directory> # You can add this to your .zshrc, etc
39+
```
40+
3) Optional Configuration variables
41+
```
42+
export BLOCKNODE_SERVER_CONSUMER_TIMEOUT_THRESHOLD="<NumberInMiliseconds>" #Default is 1500
43+
```
44+
3745
3) ./gradlew run # ./gradlew run --debug-jvm to run in debug mode
3846

3947
# Running Tests
40-
1) ./gradlew build
48+
1) ./gradlew build

server/src/main/java/com/hedera/block/server/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ private Constants() {}
2424

2525
// Config Constants
2626
public static final String BLOCKNODE_STORAGE_ROOT_PATH_KEY = "blocknode.storage.root.path";
27+
public static final String BLOCKNODE_SERVER_CONSUMER_TIMEOUT_THRESHOLD_KEY = "blocknode.server.consumer.timeout.threshold";
2728

2829
// Constants specified in the service definition of the .proto file
2930
public static final String SERVICE_NAME = "BlockStreamGrpc";

server/src/main/java/com/hedera/block/server/Server.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ public static void main(final String[] args) {
5959
final Config config = Config.create();
6060
Config.global(config);
6161

62+
// Get Timeout threshold from configuration
63+
final long consumerTimeoutThreshold = config.get(BLOCKNODE_SERVER_CONSUMER_TIMEOUT_THRESHOLD_KEY).asLong().orElse(1500L);
64+
6265
// Initialize the block storage, cache, and service
6366
final BlockStorage<BlockStreamServiceGrpcProto.Block> blockStorage = new FileSystemBlockStorage(BLOCKNODE_STORAGE_ROOT_PATH_KEY, config);
64-
65-
// TODO: Make timeoutThresholdMillis configurable
66-
final BlockStreamService blockStreamService = new BlockStreamService(1500,
67+
final BlockStreamService blockStreamService = new BlockStreamService(consumerTimeoutThreshold,
6768
new LiveStreamMediatorImpl(new WriteThroughCacheHandler(blockStorage)));
6869

6970
// Start the web server
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/test-context
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use an official Ubuntu base image
2+
FROM ubuntu:latest
3+
4+
# Set environment variables
5+
ENV DEBIAN_FRONTEND=noninteractive
6+
ENV GRPC_SERVER="host.docker.internal:8080"
7+
ENV GRPC_METHOD="BlockStreamGrpc/StreamSource"
8+
ENV PATH_TO_PROTO="/usr/local/protos/blockstream.proto"
9+
ENV PROTO_IMPORT_PATH="/usr/local/protos"
10+
ENV INITIAL_LATENCY=500
11+
ENV JITTER=500
12+
ENV BANDWIDTH=64
13+
ENV INCREASE_TIME=10
14+
ENV MAX_LATENCY=12000
15+
16+
# Install required packages
17+
RUN apt-get update && \
18+
apt-get install -y iproute2 iputils-ping curl net-tools iperf3 iptables kmod && \
19+
curl -L https://github.com/fullstorydev/grpcurl/releases/download/v1.8.7/grpcurl_1.8.7_linux_x86_64.tar.gz -o grpcurl.tar.gz && \
20+
tar -xvf grpcurl.tar.gz && mv grpcurl /usr/local/bin/grpcurl && rm grpcurl.tar.gz
21+
22+
# Copy scripts and protos folder into the container
23+
COPY configure_latency.sh start.sh test-context/consumer.sh /usr/local/bin/
24+
COPY test-context/protos /usr/local/protos
25+
26+
# Default command to run when starting the container
27+
CMD ["bash", "-c", "start.sh"]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Network Latency Simulator
2+
3+
Due to the asynchronous nature of the Streaming Service, it is important to test the system under different network conditions, such as high latency, packet loss, and jitter. This tool allows you to simulate different network conditions by adding latency, packet loss, low bandwidth and jitter to the network traffic.
4+
5+
And making sure that a single `consumer` that is experiencing network issues does not affect the other `consumers` that are consuming the same stream from the BlockNode.
6+
7+
This test aims to make sure that the system is resilient to network issues and that a single consumer that is experiencing network issues does not affect the other consumers that are consuming the same stream from the BlockNode.
8+
9+
## Running Locally
10+
11+
1. Move to the `network-latency-simulator` directory.
12+
2. Prepare the Test Context, Build the Docker Image with the network latency simulator.
13+
```bash
14+
cd server/src/test/network-latency-simulator
15+
16+
./setup.sh
17+
18+
docker build -t network-latency-simulator .
19+
```
20+
21+
3. Start the BlockNode Server. (Follow instructions on main README.md)
22+
- Due to the Latency, the consumers might be disconnected from the BlockNode, since the current timeout is 1500 ms, you should increase the timeout to 100000ms to be able to correctly test the network issues. (see main README.md of the server for more details on how to change the timeout)
23+
4. Start the producer and a single consumer (this consumer will be the control one without any network issues).
24+
```bash
25+
/server/src/test/resources/producer.sh 1 1000 # this will produce 1000 blocks
26+
/server/src/test/resources/consumer.sh 1 1000 # this will consume 1000 blocks
27+
```
28+
5. Start the consumer inside the network latency simulator container, you can start as many as you want.
29+
```bash
30+
docker run -it --cap-add=NET_ADMIN network-latency-simulator
31+
```
32+
33+
The consumer inside the container will start consuming the blocks from the BlockNode, and you can see the network issues being simulated.
34+
The network latency simulator will simulate the following network issues:
35+
- Latency, increases every 10 seconds (by default) by 1000ms
36+
- Packet Loss (Drops 10% of the packets)
37+
- Low Bandwidth, limits the bandwidth to 64kbps.
38+
- Jitter, adds 500ms of jitter (latency variability) to the network.
39+
40+
There are some environment variables that you can set to change the behavior of the network latency simulator:
41+
42+
**configure_latency.sh:**
43+
- `LATENCY_INCREASE_INTERVAL`: The interval in seconds to increase the latency, default is 10 seconds.
44+
- `INITIAL_LATENCY`: The initial latency to start with, default is 500ms, once the MAX latency is reached, it will reset to the initial latency.
45+
- `JITTER`: The jitter to add to the network, default is 500ms.
46+
- `BANDWIDTH`: The bandwidth to limit the network to, default is 64kbps.
47+
- `INCREASE_TIME`: The time in seconds to increase the latency, default is 10 (seconds).
48+
- `MAX_LATENCY`: The maximum latency to reach, default is 12000 (ms).
49+
50+
**consumer.sh:**
51+
- `GRPC_SERVER`: The gRPC server to connect to, default is `host.docker.internal:8080`, connects to the host BlockNode.
52+
- `GRPC_METHOD`: The gRPC method to call, default is `BlockStreamGrpc/StreamSource`.
53+
- `PATH_TO_PROTO`: The path to the proto file, default is `/usr/local/protos/blockstream.proto` (inside the container).
54+
- `PROTO_IMPORT_PATH`: The import path of the proto file, default is `/usr/local/protos` (inside the container).
55+
56+
Example of how to set the environment variables when running the container:
57+
```bash
58+
docker run -it --cap-add=NET_ADMIN -e LATENCY_INCREASE_INTERVAL=5 -e INITIAL_LATENCY=1000 -e JITTER=1000 -e BANDWIDTH=128 -e INCREASE_TIME=5 -e MAX_LATENCY=10000 network-latency-simulator
59+
```
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/bin/bash
2+
3+
# Default values
4+
DEFAULT_INITIAL_LATENCY=1000
5+
DEFAULT_JITTER=500
6+
DEFAULT_BANDWIDTH=64
7+
DEFAULT_INCREASE_TIME=10
8+
MAX_LATENCY=12000
9+
PACKET_LOSS=5 # Packet loss in percentage
10+
11+
# Parameters with default values
12+
INITIAL_LATENCY=${INITIAL_LATENCY:-$DEFAULT_INITIAL_LATENCY}
13+
JITTER=${JITTER:-$DEFAULT_JITTER}
14+
BANDWIDTH=${BANDWIDTH:-$DEFAULT_BANDWIDTH}
15+
INCREASE_TIME=${INCREASE_TIME:-$DEFAULT_INCREASE_TIME}
16+
CURRENT_LATENCY=$INITIAL_LATENCY
17+
MAX_LATENCY=${MAX_LATENCY:-$MAX_LATENCY}
18+
PACKET_LOSS=${PACKET_LOSS:-$PACKET_LOSS}
19+
20+
# Function to apply network configuration
21+
apply_tc_config() {
22+
# Remove any existing qdisc configuration on eth0
23+
tc qdisc del dev eth0 root 2>/dev/null
24+
# Apply the new latency, jitter, and packet loss configuration
25+
tc qdisc add dev eth0 root handle 1: netem delay ${CURRENT_LATENCY}ms ${JITTER}ms distribution normal loss ${PACKET_LOSS}%
26+
# Apply the bandwidth limitation
27+
tc qdisc add dev eth0 parent 1:1 handle 10: tbf rate ${BANDWIDTH}kbit burst 32kbit latency 50ms
28+
echo "Updated configuration: Latency = ${CURRENT_LATENCY}ms, Jitter = ${JITTER}ms, Bandwidth = ${BANDWIDTH}kbit, Packet Loss = ${PACKET_LOSS}% - (distribution normal)"
29+
}
30+
31+
# Initial configuration
32+
apply_tc_config
33+
echo "Initial configuration applied: Latency = ${CURRENT_LATENCY}ms, Jitter = ${JITTER}ms, Bandwidth = ${BANDWIDTH}kbit, Packet Loss = ${PACKET_LOSS}% - (distribution normal)"
34+
35+
while true; do
36+
sleep $INCREASE_TIME
37+
CURRENT_LATENCY=$((CURRENT_LATENCY + 1000))
38+
if [ $CURRENT_LATENCY -gt $MAX_LATENCY ]; then
39+
CURRENT_LATENCY=$INITIAL_LATENCY
40+
fi
41+
apply_tc_config
42+
done
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
echo "Setting up test-context"
4+
5+
mkdir -p test-context
6+
mkdir -p test-context/protos
7+
8+
cp -R ../../../../protos/src/main/protobuf/*.proto test-context/protos/
9+
cp ../resources/consumer.sh test-context/
10+
11+
# Make sure to make scripts executable
12+
chmod +x test-context/consumer.sh start.sh configure_latency.sh
13+
14+
echo "Successfully set up test-context"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Print ENV Values
2+
echo "----- Configuration of Consumer Variables: -----"
3+
echo "GRPC_SERVER: $GRPC_SERVER"
4+
echo "GRPC_METHOD: $GRPC_METHOD"
5+
echo "PATH_TO_PROTO: $PATH_TO_PROTO"
6+
echo "PROTO_IMPORT_PATH: $PROTO_IMPORT_PATH"
7+
echo "----- Configuration of Latency Variables: -----"
8+
echo "INITIAL_LATENCY: $INITIAL_LATENCY"
9+
echo "JITTER: $JITTER"
10+
echo "BANDWIDTH: $BANDWIDTH"
11+
echo "INCREASE_TIME: $INCREASE_TIME"
12+
echo "MAX_LATENCY: $MAX_LATENCY"
13+
echo "PACKET_LOSS: $PACKET_LOSS"
14+
15+
# First Start consumer without any network latency so it connects without issues.
16+
consumer.sh 1 1000 &
17+
# Then start the network latency configuration script.
18+
configure_latency.sh

server/src/test/resources/consumer.sh

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ if [ "$#" -eq 2 ]; then
2121
echo "The optional positive integer is: $2"
2222
fi
2323

24-
GRPC_SERVER="localhost:8080"
25-
GRPC_METHOD="BlockStreamGrpc/StreamSource"
26-
PATH_TO_PROTO="../../../../protos/src/main/protobuf/blockstream.proto"
24+
# Use environment variables or default values
25+
GRPC_SERVER=${GRPC_SERVER:-"localhost:8080"}
26+
GRPC_METHOD=${GRPC_METHOD:-"BlockStreamGrpc/StreamSource"}
27+
PATH_TO_PROTO=${PATH_TO_PROTO:-"../../../../protos/src/main/protobuf/blockstream.proto"}
28+
PROTO_IMPORT_PATH=${PROTO_IMPORT_PATH:-"../../../../protos/src/main/protobuf"}
2729

2830
echo "Starting consumer..."
2931

@@ -54,5 +56,5 @@ trap cleanup SIGINT
5456
sleep 1
5557

5658
done
57-
) | grpcurl -plaintext -proto $PATH_TO_PROTO -d @ $GRPC_SERVER $GRPC_METHOD
59+
) | grpcurl -plaintext -import-path $PROTO_IMPORT_PATH -proto $PATH_TO_PROTO -d @ $GRPC_SERVER $GRPC_METHOD
5860

0 commit comments

Comments
 (0)