Skip to content
Open
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
55 changes: 55 additions & 0 deletions scripts/perf-lab/app-load-test.hf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# =============================================================================
# Hyperfoil Load Test Configuration
# =============================================================================
#
# This configuration file defines a load test scenario for the application
# endpoint. It includes a warmup phase followed by a main load test phase,
# both targeting the same HTTP endpoint.
#
# Configurable Parameters:
# ----------------------------------
# PROTOCOL - HTTP protocol to use (default: http)
# Options: http, https
# HOST - Target server hostname (default: localhost)
# PORT - Target server port (default: 8080)
# SHARED_CONNECTIONS - Number of shared HTTP connections in the pool (default: 100)
# PATH - HTTP endpoint path to test (default: /fruits)
# WARMUP_USERS - Number of concurrent users during warmup phase (default: 2)
# WARMUP_DURATION - Duration of the warmup phase (default: 2m)
# LOAD_USERS - Number of concurrent users during main load test (default: 2)
# LOAD_DURATION - Duration of the main load test phase (default: 30s)
#
# Usage:
# ------
# Override parameters using: -P PARAM_NAME=value
# Example: jbang run@hyperfoil -PHOST=example.com -PPORT=443 -PPROTOCOL=https app-load-test.hf.yml
#
# =============================================================================

name: app-load-test
http:
- protocol: !param PROTOCOL http
host: !param HOST localhost
port: !param PORT 8080
sharedConnections: !param SHARED_CONNECTIONS 100

phases:
- loadTest:
always:
users: !param LOAD_USERS 50
duration: !param LOAD_DURATION 30s
startAfter: warmup
scenario:
- loadTest: &loadTest
- httpRequest:
GET: !param PATH /fruits
headers:
accept: application/json

- warmup:
always:
users: !param WARMUP_USERS 50
duration: !param WARMUP_DURATION 2m
scenario:
- loadTest: *loadTest

93 changes: 68 additions & 25 deletions scripts/perf-lab/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ scripts:
- sh: echo $(realpath ~)
- set-state: RUN.BASE_DIR
- set-state: RUN.REPO_DIR ${{BASE_DIR}}/spring-quarkus-perf-comparison
- set-state: RUN.HYPERFOIL_LOGS_DIR ${{REPO_DIR}}/logs/hyperfoil
- set-state: RUN.PROJ_REPO_DIR ${{REPO_DIR}}/${{PROJ_REPO_NAME}}
- set-state: RUN.SCRIPTS_DIR ${{PROJ_REPO_DIR}}/scripts/perf-lab
- set-state: RUN.HELPER_SCRIPTS_DIR ${{SCRIPTS_DIR}}/scripts
Expand All @@ -159,6 +160,7 @@ scripts:
"ENV: ${{env}}"
"BASE_DIR: ${{BASE_DIR}}"
"REPO_DIR: ${{REPO_DIR}}"
"HYPERFOIL_LOGS_DIR: ${{HYPERFOIL_LOGS_DIR}}"
"SCRIPTS_DIR: ${{SCRIPTS_DIR}}"
"SPRING3_BOOT_DIR: ${{SPRING3_BOOT_DIR}}"
"SPRING4_BOOT_DIR: ${{SPRING4_BOOT_DIR}}"
Expand All @@ -183,6 +185,7 @@ scripts:
with:
command: rm -Rf ${{REPO_DIR}}/logs
- sh: mkdir -p ${{REPO_DIR}}/logs
- sh: mkdir -p ${{HYPERFOIL_LOGS_DIR}}

clone-repo:
- script: sudo
Expand Down Expand Up @@ -409,6 +412,7 @@ scripts:
- log: Running workload
- sh: cd ${{RUNTIME.dir}}
- sh: ${{RUNTIME.buildCmd}}
- queue-download: ${{HYPERFOIL_LOGS_DIR}}
- set-state:
key: APP_LOG_REGEX
value: ${{RUNTIME.logFileStartedRegex}}
Expand All @@ -418,9 +422,9 @@ scripts:
key: LOG_FILE
value: ${{REPO_DIR}}/logs/load-test-${{RUNTIME.name}}-${{ITERATION}}.log
- sh: touch ${{LOG_FILE}}
- queue-download: ${{REPO_DIR}}/logs/wrk-warmup-${{RUNTIME.name}}-${{ITERATION}}.log
- sh: mkdir -p ${{HYPERFOIL_LOGS_DIR}}/${{RUNTIME.name}}-${{ITERATION}}
- queue-download: ${{REPO_DIR}}/logs/hf-${{RUNTIME.name}}-${{ITERATION}}.log
- queue-download: ${{LOG_FILE}}
- queue-download: ${{REPO_DIR}}/logs/wrk-${{RUNTIME.name}}-${{ITERATION}}.log
- script: start-test-services
- script: sync-drop-fs-cache
- set-signal: LOAD_STEADY_STATE_START 1
Expand Down Expand Up @@ -459,39 +463,78 @@ scripts:
log_file: ${{LOG_FILE}}
log_regex: ${{APP_LOG_REGEX}}
- wait-for: LOG_REGEX_REACHED
- sh: ${{RUN.WRK_BIN}} -t 2 -c 100 -d 2m --timeout 2m ${{TARGET_URL}} 2>&1 >${{REPO_DIR}}/logs/wrk-warmup-${{RUNTIME.name}}-${{ITERATION}}.log
- regex: unable to connect
then:
- abort: unable to connect to target application
- sleep: 30s # Wait for the app to complete warmup
- signal: LOAD_STEADY_STATE_START
- sh: ${{RUN.WRK_BIN}} -t 2 -c 100 -d 30s --timeout 30s ${{TARGET_URL}} 2>&1 >${{REPO_DIR}}/logs/wrk-${{RUNTIME.name}}-${{ITERATION}}.log
- sh: cat ${{REPO_DIR}}/logs/wrk-${{RUNTIME.name}}-${{ITERATION}}.log | grep "Requests/sec" | awk '{print $2}'
- sh: |
${{LOAD_GEN_CMD_PREFIX}} jbang \
-Dio.hyperfoil.rootdir=${{HYPERFOIL_LOGS_DIR}}/${{RUNTIME.name}}-${{ITERATION}} \
-R \
-XX:+UseNUMA \
run@hyperfoil \
-d ${{RUNTIME.name}}-${{ITERATION}} \
-o ${{HYPERFOIL_LOGS_DIR}}/${{RUNTIME.name}}-${{ITERATION}} \
${{SCRIPTS_DIR}}/app-load-test.hf.yml 2>&1 >${{REPO_DIR}}/logs/hf-${{RUNTIME.name}}-${{ITERATION}}.log
- sh: cat ${{REPO_DIR}}/logs/hf-${{RUNTIME.name}}-${{ITERATION}}.log | grep "Started run" | awk '{print $3}'
then:
- set-state: THROUGHPUT
- script: state-array-push
with:
array: RUN.output.results.${{RUNTIME.name}}.load.throughput
value: ${{THROUGHPUT}}
- sh: cat ${{REPO_DIR}}/logs/wrk-${{RUNTIME.name}}-${{ITERATION}}.log | grep "Socket errors:" | sed -E 's/.*connectionErrors ([0-9]+).*/\1/'
- set-state:
key: RUN_NUM
autoConvert: false

- sh: cat ${{HYPERFOIL_LOGS_DIR}}/${{RUNTIME.name}}-${{ITERATION}}/run/${{RUN_NUM}}/all.json
then:
- set-state: HF_OUTPUT
- read-state: ${{HF_OUTPUT}}
then:
- regex: ^$
- json: $.info.startTime
then:
- set-state: CONNECTION_ERRORS 0
- set-state: START_TIME
else:
- abort: Unable to find startTime in ${{HYPERFOIL_LOGS_DIR}}/${{RUNTIME.name}}-${{ITERATION}}/run/${{RUN_NUM}}/all.json
- read-state: ${{HF_OUTPUT}}
then:
- json: $.info.terminateTime
then:
- set-state: TERMINATE_TIME
else:
- abort: Unable to find terminateTime in ${{HYPERFOIL_LOGS_DIR}}/${{RUNTIME.name}}-${{ITERATION}}/run/${{RUN_NUM}}/all.json
- read-state: ${{HF_OUTPUT}}
then:
- json: $.stats[?(@.phase == 'loadTest')].total.summary
then:
- set-state: SUMMARY
else:
- abort: Unable to find summary in ${{HYPERFOIL_LOGS_DIR}}/${{RUNTIME.name}}-${{ITERATION}}/run/${{RUN_NUM}}/all.json
- read-state: ${{SUMMARY}}
then:
- json: $.requestCount
then:
- set-state: REQUEST_COUNT
else:
- abort: Unable to find request count in ${{HYPERFOIL_LOGS_DIR}}/${{RUNTIME.name}}-${{ITERATION}}/run/${{RUN_NUM}}/all.json
- read-state: ${{SUMMARY}}
then:
- json: $.connectionErrors
then:
- set-state: CONNECTION_ERRORS
else:
- set-state: CONNECTION_ERRORS 0
- read-state: ${{SUMMARY}}
then:
- json: $.requestTimeouts
then:
- set-state: REQUEST_TIMEOUTS
else:
- set-state: REQUEST_TIMEOUTS 0
- set-state:
key: THROUGHPUT
value: ${{= ${{REQUEST_COUNT}}/((${{TERMINATE_TIME}}-${{START_TIME}})/1000) }}
- script: state-array-push
with:
array: RUN.output.results.${{RUNTIME.name}}.load.throughput
value: ${{THROUGHPUT}}
- script: state-array-push
with:
array: RUN.output.results.${{RUNTIME.name}}.load.connectionErrors
value: ${{CONNECTION_ERRORS}}

- sh: cat ${{REPO_DIR}}/logs/wrk-${{RUNTIME.name}}-${{ITERATION}}.log | grep "Socket errors:" | sed -E 's/.*requestTimeouts ([0-9]+)/\1/'
then:
- regex: ^$
then:
- set-state: REQUEST_TIMEOUTS 0
else:
- set-state: REQUEST_TIMEOUTS
- script: state-array-push
with:
array: RUN.output.results.${{RUNTIME.name}}.load.requestTimeouts
Expand Down