Load Tests #140
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
concurrency: ci-$ # Only a single workflow can be executed concurrently | |
name: Load Tests | |
on: # yamllint disable-line rule:truthy | |
workflow_dispatch: | |
inputs: | |
environment: | |
description: The environment to run against | |
type: choice | |
options: [dev, test] # nightly should not be initiated manually | |
type: | |
description: The type of load test to run | |
type: choice | |
options: [EOA, ERC20, ERC721, MIXED] | |
dynamic: | |
description: Indicates whether the load test should generate dynamic transactions | |
type: boolean | |
default: false | |
vus: | |
description: The number of Virtual Users | |
type: string | |
default: "1" | |
txs_per_user: | |
description: The number of transactions per Virtual User | |
type: string | |
default: "1" | |
txpool_timeout: | |
description: The timeout for waiting for the transaction pool to empty | |
type: string | |
default: "10m0s" | |
wait_txpool: | |
description: Waits for transaction pool to empty before collecting results | |
type: boolean | |
default: false | |
receipts_timeout: | |
description: The timeout for waiting for transaction receipts | |
type: string | |
default: "30s" | |
batch_size: | |
description: Batch size | |
type: string | |
default: "1" | |
notification: | |
description: Notification | |
type: boolean | |
default: true | |
workflow_call: | |
inputs: | |
environment: | |
description: The environment to run against | |
type: string | |
required: true | |
type: | |
description: The type of load test to run | |
type: string | |
required: true | |
dynamic: | |
description: Indicates whether the load test should generate dynamic transactions | |
type: boolean | |
required: true | |
vus: | |
description: The number of Virtual Users | |
type: string | |
required: true | |
txs_per_user: | |
description: The number of transactions per Virtual User | |
type: string | |
required: true | |
txpool_timeout: | |
description: The timeout for waiting for the transaction pool to empty | |
type: string | |
required: true | |
wait_txpool: | |
description: Waits for transaction pool to empty before collecting results | |
type: boolean | |
required: true | |
receipts_timeout: | |
description: The timeout for waiting for transaction receipts | |
type: string | |
required: true | |
batch_size: | |
description: Batch size | |
type: string | |
required: true | |
notification: | |
description: Notification | |
type: boolean | |
required: true | |
outputs: | |
load_test_output: | |
description: Load Test output | |
value: ${{ jobs.load_test.outputs.test_output_success }} | |
total_time: | |
description: Total Time | |
value: ${{ jobs.load_test.outputs.total_time }} | |
total_txs: | |
description: Total Transactions | |
value: ${{ jobs.load_test.outputs.total_txs }} | |
total_blocks: | |
description: Total Blocks | |
value: ${{ jobs.load_test.outputs.total_blocks }} | |
avg_txs_per_second: | |
description: Average Transactions Per Second | |
value: ${{ jobs.load_test.outputs.avg_txs_per_second }} | |
max_txs_per_second: | |
description: Maximum Transactions Per Second | |
value: ${{ jobs.load_test.outputs.max_txs_per_second }} | |
total_gas_used: | |
description: Total Gas Used | |
value: ${{ jobs.load_test.outputs.total_gas_used }} | |
avg_gas_per_tx: | |
description: Average Gas Used Per Transaction | |
value: ${{ jobs.load_test.outputs.avg_gas_per_tx }} | |
avg_gas_utilization: | |
description: Average Gas Utilization | |
value: ${{ jobs.load_test.outputs.avg_gas_utilization }} | |
max_gas_utilization: | |
description: Maximum Gas Utilization | |
value: ${{ jobs.load_test.outputs.max_gas_utilization }} | |
txpool_pending: | |
description: Pending Transactions Count | |
value: ${{ jobs.txpool_status.outputs.txpool_pending }} | |
txpool_queued: | |
description: Queued Transactions Count | |
value: ${{ jobs.txpool_status.outputs.txpool_queued }} | |
results_artifact_id: | |
description: Results Artifact ID | |
value: ${{ jobs.load_test.outputs.results_artifact_id }} | |
secrets: | |
AWS_ROLE_ARN: | |
required: true | |
AWS_S3_BLADE_BUCKET: | |
required: true | |
AWS_LOADTESTRUNNER_AMI_ID: | |
required: true | |
AWS_LOADTESTRUNNER_SUBNET_ID: | |
required: true | |
AWS_LOADTESTRUNNER_SG_ID: | |
required: true | |
AWS_LOADTESTRUNNER_MNEMONIC: | |
required: true | |
PERSONAL_ACCESS_TOKEN: | |
required: true | |
SLACK_WEBHOOK_URL: | |
required: true | |
permissions: | |
id-token: write | |
contents: read | |
security-events: write | |
jobs: | |
check_network: | |
name: Check if the network is already deployed | |
runs-on: ubuntu-latest | |
outputs: | |
rpc_url: ${{ steps.rpc_url.outputs.url }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4.1.1 | |
- name: Configure AWS Credentials | |
uses: aws-actions/configure-aws-credentials@v4.0.1 | |
with: | |
aws-region: ${{ vars.AWS_REGION }} | |
role-to-assume: ${{ secrets.AWS_ROLE_ARN }} | |
- name: Retrieve state file from s3 | |
id: retrieve_state | |
run: echo "retrieve_state_output=$(aws s3 cp s3://${{ secrets.AWS_S3_BLADE_BUCKET }}/states/${{ inputs.environment }} state.json)" >> $GITHUB_OUTPUT | |
- name: Set RPC URL | |
id: rpc_url | |
if: contains(steps.retrieve_state.outputs.retrieve_state_output, 'download') | |
run: echo "url=$(cat state.json | jq -r '.outputs.aws_lb_ext_domain.value // empty')" >> $GITHUB_OUTPUT | |
load_test_runner: | |
name: Deploy Load Test Runner | |
runs-on: ubuntu-latest | |
environment: ${{ inputs.environment }} | |
needs: check_network | |
if: needs.check_network.outputs.rpc_url != '' | |
outputs: | |
load_test_runner_label: ${{ steps.start_load_teste_runner.outputs.label }} | |
load_test_runner_instance_id: ${{ steps.start_load_teste_runner.outputs.ec2-instance-id }} | |
steps: | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4.0.1 | |
with: | |
aws-region: ${{ vars.AWS_REGION }} | |
role-to-assume: ${{ secrets.AWS_ROLE_ARN }} | |
- name: Start Load Test Runner | |
id: start_load_teste_runner | |
uses: Ethernal-Tech/ec2-github-runner@v3.0.0 | |
with: | |
mode: start | |
ec2-instance-type: ${{ vars.AWS_INSTANCE_TYPE }} | |
ec2-image-id: ${{ secrets.AWS_LOADTESTRUNNER_AMI_ID }} | |
subnet-id: ${{ secrets.AWS_LOADTESTRUNNER_SUBNET_ID }} | |
security-group-id: ${{ secrets.AWS_LOADTESTRUNNER_SG_ID }} | |
github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} | |
aws-resource-tags: > | |
[ | |
{"Key": "Name", "Value": "${{ inputs.environment }}-load-test-runner"} | |
] | |
load_test: | |
name: Run Load Test | |
runs-on: ${{ needs.load_test_runner.outputs.load_test_runner_label }} | |
needs: [check_network, load_test_runner] | |
outputs: | |
test_output_success: ${{ steps.load_test_results_success.outputs.test_output }} | |
total_time: ${{ steps.load_test_results.outputs.total_time }} | |
total_txs: ${{ steps.load_test_results.outputs.total_txs }} | |
total_blocks: ${{ steps.load_test_results.outputs.total_blocks }} | |
avg_txs_per_second: ${{ steps.load_test_results.outputs.avg_txs_per_second }} | |
max_txs_per_second: ${{ steps.load_test_results.outputs.max_txs_per_second }} | |
total_gas_used: ${{ steps.load_test_results.outputs.total_gas_used }} | |
avg_gas_per_tx: ${{ steps.load_test_results.outputs.avg_gas_per_tx }} | |
avg_gas_utilization: ${{ steps.load_test_results.outputs.avg_gas_utilization }} | |
max_gas_utilization: ${{ steps.load_test_results.outputs.max_gas_utilization }} | |
results_artifact_id: ${{ steps.artifact-upload.outputs.artifact-id }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4.1.1 | |
- name: Build Blade | |
run: make build | |
env: | |
GOPATH: /home/ubuntu/go | |
GOCACHE: /home/ubuntu/.cache/go-build | |
GOMODCACHE: /home/ubuntu/.cache/go-mod | |
- name: Start Load Test | |
id: load_test_results | |
run: | | |
./blade load-test --jsonrpc "http://${{ needs.check_network.outputs.rpc_url }}" --mnemonic "${{ secrets.AWS_LOADTESTRUNNER_MNEMONIC }}" --name "results" --type ${{ inputs.type }} ${{ inputs.dynamic == true && '--dynamic' || '' }} --vus ${{ inputs.vus }} --txs-per-user ${{ inputs.txs_per_user }} --batch-size ${{ inputs.batch_size }} --txpool-timeout ${{ inputs.txpool_timeout }} ${{ inputs.wait_txpool == true && '--wait-txpool' || '' }} --receipts-timeout ${{ inputs.receipts_timeout }} --to-json | |
echo "total_time=$(cat results_${{ inputs.type }}.json | jq -r '.totalTime')" >> $GITHUB_OUTPUT | |
echo "total_txs=$(cat results_${{ inputs.type }}.json | jq -r '.totalTxs')" >> $GITHUB_OUTPUT | |
echo "total_blocks=$(cat results_${{ inputs.type }}.json | jq -r '.totalBlocks')" >> $GITHUB_OUTPUT | |
echo "avg_txs_per_second=$(cat results_${{ inputs.type }}.json | jq -r '.avgTxsPerSecond')" >> $GITHUB_OUTPUT | |
echo "max_txs_per_second=$(cat results_${{ inputs.type }}.json | jq -r '.maxTxsPerSecond')" >> $GITHUB_OUTPUT | |
echo "total_gas_used=$(cat results_${{ inputs.type }}.json | jq -r '.totalGasUsed')" >> $GITHUB_OUTPUT | |
echo "avg_gas_per_tx=$(cat results_${{ inputs.type }}.json | jq -r '.avgGasPerTx')" >> $GITHUB_OUTPUT | |
echo "avg_gas_utilization=$(cat results_${{ inputs.type }}.json | jq -r '.avgGasUtilization')" >> $GITHUB_OUTPUT | |
echo "max_gas_utilization=$(cat results_${{ inputs.type }}.json | jq -r '.maxGasUtilization')" >> $GITHUB_OUTPUT | |
- name: Run tests success | |
if: success() | |
id: load_test_results_success | |
run: echo "test_output=true" >> $GITHUB_OUTPUT | |
- name: Upload Artifact | |
uses: actions/upload-artifact@v4.3.0 | |
id: artifact-upload | |
with: | |
name: results_${{ inputs.type }}.tar.gz | |
path: results_${{ inputs.type }}.json | |
retention-days: 3 | |
txpool_status: | |
name: Check txpool status after Load Test | |
runs-on: ubuntu-latest | |
needs: [check_network, load_test] | |
outputs: | |
txpool_pending: ${{ steps.txpool_status_results.outputs.txpool_pending }} | |
txpool_queued: ${{ steps.txpool_status_results.outputs.txpool_queued }} | |
steps: | |
- name: Get txpool status | |
id: txpool_status_results | |
run: | | |
txpool_status=$(curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"txpool_status","params":[],"id":1}' http://${{ needs.check_network.outputs.rpc_url }} | jq -r .result) | |
echo "txpool_pending=$(echo $txpool_status | jq -r '.pending')" >> $GITHUB_OUTPUT | |
echo "txpool_queued=$(echo $txpool_status | jq -r '.queued')" >> $GITHUB_OUTPUT | |
notification: | |
name: Load Test Notification | |
needs: [load_test, txpool_status] | |
uses: ./.github/workflows/notification-load-test.yml | |
if: (always() && inputs.notification && needs.load_test.outputs.test_output_success == 'true') | |
with: | |
environment: ${{ inputs.environment }} | |
type: ${{ inputs.type }} | |
dynamic: ${{ inputs.dynamic }} | |
vus: ${{ inputs.vus }} | |
txs_per_user: ${{ inputs.txs_per_user }} | |
txpool_timeout: ${{ inputs.txpool_timeout }} | |
wait_txpool: ${{ inputs.wait_txpool }} | |
receipts_timeout: ${{ inputs.receipts_timeout }} | |
batch_size: ${{ inputs.batch_size }} | |
total_time: ${{ needs.load_test.outputs.total_time }} | |
total_txs: ${{ needs.load_test.outputs.total_txs }} | |
total_blocks: ${{ needs.load_test.outputs.total_blocks }} | |
avg_txs_per_second: ${{ needs.load_test.outputs.avg_txs_per_second }} | |
max_txs_per_second: ${{ needs.load_test.outputs.max_txs_per_second }} | |
total_gas_used: ${{ needs.load_test.outputs.total_gas_used }} | |
avg_gas_per_tx: ${{ needs.load_test.outputs.avg_gas_per_tx }} | |
avg_gas_utilization: ${{ needs.load_test.outputs.avg_gas_utilization }} | |
max_gas_utilization: ${{ needs.load_test.outputs.max_gas_utilization }} | |
txpool_pending: ${{ needs.txpool_status.outputs.txpool_pending }} | |
txpool_queued: ${{ needs.txpool_status.outputs.txpool_queued }} | |
results_artifact_id: ${{ needs.load_test.outputs.results_artifact_id }} | |
secrets: | |
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
destroy_load_test_runner: | |
name: Destroy Load Test Runner | |
environment: ${{ inputs.environment }} | |
needs: [load_test_runner, load_test, txpool_status] | |
if: always() | |
runs-on: ubuntu-latest | |
steps: | |
- name: Configure AWS credentials | |
uses: aws-actions/configure-aws-credentials@v4.0.1 | |
with: | |
aws-region: ${{ vars.AWS_REGION }} | |
role-to-assume: ${{ secrets.AWS_ROLE_ARN }} | |
- name: Stop Load Test Runner | |
uses: Ethernal-Tech/ec2-github-runner@v3.0.0 | |
with: | |
mode: stop | |
label: ${{ needs.load_test_runner.outputs.load_test_runner_label }} | |
ec2-instance-id: ${{ needs.load_test_runner.outputs.load_test_runner_instance_id }} | |
github-token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} |