diff --git a/.github/scripts/run-benchmark-client.sh b/.github/scripts/run-benchmark-client.sh new file mode 100644 index 000000000..d0d4dace0 --- /dev/null +++ b/.github/scripts/run-benchmark-client.sh @@ -0,0 +1,93 @@ +#!/bin/bash +set -euo pipefail + +# benchmark over ssh +# +# MagicOnion Client +# $ ssh -o StrictHostKeyChecking=accept-new -i ~/.ssh/id_ed25519 azure-user@4.215.237.255 'bash -s -- --args "-u http://${BENCHMARK_SERVER_NAME}:5000 -s streaminghub --channels 1 --streams 1"' < ./scripts/run-benchmark.sh +# $ echo $? + +function usage { + echo "usage: $(basename $0) --args <string> [options]" + echo "Required:" + echo " --args string Arguments to pass when running the built binary (default: \"\")" + echo "Options:" + echo " --help Show this help message" +} + +while [ $# -gt 0 ]; do + case $1 in + # required + --args) _ARGS=$2; shift 2; ;; + # optional + --help) usage; exit 1; ;; + *) shift ;; + esac +done + +function print() { + echo "" + echo "$*" +} + +# parameter setup +repo="MagicOnion" +build_config="Release" +args="${_ARGS:=""}" +build_csproj="perf/BenchmarkApp/PerformanceTest.Client/PerformanceTest.Client.csproj" +env_settings="" + +binary_name=$(basename "$(dirname "$build_csproj")") +publish_dir="artifacts/$binary_name" +clone_path="$HOME/github/$repo" +output_dir="$clone_path/$publish_dir" +full_process_path="$output_dir/$binary_name" + +# show machine name +print "MACHINE_NAME: $(hostname)" + +# is dotnet installed? +print "# Show installed dotnet sdk versions" +echo "dotnet sdk versions (list): $(dotnet --list-sdks)" +echo "dotnet sdk version (default): $(dotnet --version)" + +# setup env +print "# Setup environment" +IFS=';' read -ra env_array <<< "$env_settings" +for item in "${env_array[@]}"; do + if [ -n "$item" ]; then + export "$item" + fi +done + +# dotnet publish +print "# dotnet publish $build_csproj" +pushd "$clone_path" + print " ## list current files under $(pwd)" + ls -l + + print " ## dotnet publish $build_csproj" + dotnet publish -c "$build_config" -p:PublishSingleFile=true --runtime linux-x64 --self-contained false "$build_csproj" -o "$publish_dir" + + print " ## list published files under $publish_dir" + ls "$publish_dir" + + print " ## add +x permission to published file $full_process_path" + chmod +x "$full_process_path" +popd + +# process check +print "# Checking process $binary_name already runnning, kill if exists" +ps -eo pid,cmd | while read -r pid cmd; do + if echo "$cmd" | grep -E "^./$binary_name" >/dev/null 2>&1; then + echo "Found & killing process $pid ($cmd)" + kill "$pid" + fi +done + +# run dotnet app +print "# Run $full_process_path $args" +pushd "$output_dir" + # run foreground + "./$binary_name" $args +popd diff --git a/.github/scripts/run-benchmark-server.sh b/.github/scripts/run-benchmark-server.sh new file mode 100644 index 000000000..271388b7b --- /dev/null +++ b/.github/scripts/run-benchmark-server.sh @@ -0,0 +1,103 @@ +#!/bin/bash +set -euo pipefail + +# benchmark over ssh +# +# MagicOnion Server +# ssh -o StrictHostKeyChecking=accept-new -i ~/.ssh/id_ed25519 azure-user@4.215.238.2 'bash -s -- ' < ./scripts/run-server +# $ echo $? + +function usage { + echo "usage: $(basename $0) [options]" + echo "Options:" + echo " --help Show this help message" +} + +while [ $# -gt 0 ]; do + case $1 in + --help) usage; exit 1; ;; + *) shift ;; + esac +done + +function print() { + echo "" + echo "$*" +} + +# parameter setup +repo="MagicOnion" +build_config="Release" +build_csproj="perf/BenchmarkApp/PerformanceTest.Server/PerformanceTest.Server.csproj" +env_settings="" + +binary_name=$(basename "$(dirname "$build_csproj")") +publish_dir="artifacts/$binary_name" +clone_path="$HOME/github/$repo" +output_dir="$clone_path/$publish_dir" +full_process_path="$output_dir/$binary_name" + +stdoutfile="stdout.log" +stderrfile="stderr.log" + +# show machine name +print "MACHINE_NAME: $(hostname)" + +# is dotnet installed? +print "# Show installed dotnet sdk versions" +echo "dotnet sdk versions (list): $(dotnet --list-sdks)" +echo "dotnet sdk version (default): $(dotnet --version)" + +# setup env +print "# Setup environment" +IFS=';' read -ra env_array <<< "$env_settings" +for item in "${env_array[@]}"; do + if [ -n "$item" ]; then + export "$item" + fi +done + +# process check +print "# Checking process $binary_name already runnning, kill if exists" +ps -eo pid,cmd | while read -r pid cmd; do + if echo "$cmd" | grep -E "^./$binary_name" >/dev/null 2>&1; then + echo "Found & killing process $pid ($cmd)" + kill "$pid" + fi +done + +# dotnet publish +print "# dotnet publish $build_csproj" +pushd "$clone_path" + print " ## list current files under $(pwd)" + ls -l + + print " ## dotnet publish $build_csproj" + dotnet publish -c "$build_config" -p:PublishSingleFile=true --runtime linux-x64 --self-contained false "$build_csproj" -o "$publish_dir" + + print " ## list published files under $publish_dir" + ls "$publish_dir" + + print " ## add +x permission to published file $full_process_path" + chmod +x "$full_process_path" +popd + +# run dotnet app +print "# Run $full_process_path" +pushd "$output_dir" + # run background https://stackoverflow.com/questions/29142/getting-ssh-to-execute-a-command-in-the-background-on-target-machine + nohup "./$binary_name" > "${stdoutfile}" 2> "${stderrfile}" < /dev/null & + + # wait 10s will be enough to start the server or not + sleep 10s + + # output stdout + cat "${stdoutfile}" + + # output stderr + if [[ "$(stat -c%s "$stderrfile")" -ne "0" ]]; then + echo "Error found when running the server." + cat "${stderrfile}" + exit 1 + fi +popd diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 000000000..ef00c6be7 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,44 @@ +name: Benchmark + +on: + issue_comment: + types: [created] + workflow_dispatch: + +permissions: + contents: read + id-token: write + +jobs: + prepare: + outputs: + branch: ${{ steps.get-branch.outputs.name }} + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - name: Get branch name + id: get-branch + run: | + if [[ "${{ github.event_name}}" == "issue_comment" && "${{ github.event.issue.pull_request.html_url }}" != "" ]]; then + # issue_comment (pull_request) + branch=$(gh pr view "${{ github.event.issue.pull_request.html_url }}" --json headRefName | jq -r ".headRefName") + echo "name=${branch}" | tee -a "$GITHUB_OUTPUT" + else + # workflow_dispatch or issue_comment (issue) + echo "name=${{ github.ref_name }}" | tee -a "$GITHUB_OUTPUT" + fi + + # run benchmark + benchmark: + needs: [prepare] + uses: Cysharp/Actions/.github/workflows/benchmark.yaml@main + with: + branch: ${{ needs.prepare.outputs.branch }} + dotnet-version: "8.0" + environment: benchmark + client-benchmark-script-path: ".github/scripts/run-benchmark-client.sh" + client-benchmark-script-args: "--args \"-u http://${BENCHMARK_SERVER_NAME}:5000 -s streaminghub --channels 1 --streams 1\"" + server-benchmark-script-path: ".github/scripts/run-benchmark-server.sh" + server-benchmark-script-args: "" + secrets: inherit diff --git a/perf/BenchmarkApp/PerformanceTest.Server/Properties/launchSettings.json b/perf/BenchmarkApp/PerformanceTest.Server/Properties/launchSettings.json index 2151c9d5c..0d2e73873 100644 --- a/perf/BenchmarkApp/PerformanceTest.Server/Properties/launchSettings.json +++ b/perf/BenchmarkApp/PerformanceTest.Server/Properties/launchSettings.json @@ -6,7 +6,8 @@ "launchBrowser": false, "applicationUrl": "http://localhost:5000;https://localhost:5001", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "Kestrel__Endpoints__Grpc__Url": "http://localhost:5000" } } } diff --git a/perf/BenchmarkApp/PerformanceTest.Server/appsettings.json b/perf/BenchmarkApp/PerformanceTest.Server/appsettings.json index 1aef5074f..02b5fa21c 100644 --- a/perf/BenchmarkApp/PerformanceTest.Server/appsettings.json +++ b/perf/BenchmarkApp/PerformanceTest.Server/appsettings.json @@ -9,6 +9,12 @@ "Kestrel": { "EndpointDefaults": { "Protocols": "Http2" + }, + "Endpoints": { + "Grpc": { + "Url": "http://+:5000", + "Protocols": "Http2" + } } } }