forked from erigontech/erigon
-
Notifications
You must be signed in to change notification settings - Fork 0
403 lines (351 loc) · 16.3 KB
/
qa-rpc-performance-tests.yml
File metadata and controls
403 lines (351 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
name: QA - RPC Performance Tests
on:
push:
branches:
- 'release/3.*'
workflow_dispatch:
inputs:
run_geth:
description: 'Run the Geth benchmark besides the Erigon one'
type: boolean
required: false
default: false
schedule:
- cron: '0 3 * * *' # Run daily at 03:00 UTC
concurrency:
group: rpc-performance-historic-${{ github.ref }}
cancel-in-progress: false
env:
CHAIN: mainnet
ERIGON_QA_PATH: /home/qarunner/erigon-qa
RPC_PAST_TEST_DIR: /opt/rpc-past-tests
jobs:
setup:
name: Setup benchmark run
runs-on: [self-hosted, qa]
permissions:
contents: read
outputs:
run_id: ${{ steps.vars.outputs.run_id }}
network: ${{ steps.vars.outputs.network }}
run_geth: ${{ steps.vars.outputs.run_geth }}
steps:
- name: Compute shared variables
id: vars
shell: bash
run: |
set -euo pipefail
# Unique namespace for this workflow execution
RUN_ID="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
# Decide whether to run geth:
# - schedule: only on Sunday (UTC)
# - workflow_dispatch: controlled by input
RUN_GETH="false"
if [ "${{ github.event_name }}" = "schedule" ]; then
DOW="$(date -u +%w)"
if [ "$DOW" = "0" ]; then # Sunday, (0..6 = Sun..Sat)
RUN_GETH="true"
fi
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
if [ "${{ github.event.inputs.run_geth }}" = "true" ]; then
RUN_GETH="true"
fi
fi
# Variables
echo "run_id=${RUN_ID}" >> "$GITHUB_OUTPUT"
echo "network=${{ env.CHAIN }}" >> "$GITHUB_OUTPUT"
echo "run_geth=${RUN_GETH}" >> "$GITHUB_OUTPUT"
- name: Print configuration (for logs)
shell: bash
run: |
set -euo pipefail
echo "Run ID: ${{ steps.vars.outputs.run_id }}"
echo "Network: ${{ steps.vars.outputs.network }}"
echo "Run Erigon: true"
echo "Run Geth: ${{ steps.vars.outputs.run_geth }}"
test:
name: Benchmark Historic ${{ matrix.client }}
needs: [setup]
strategy:
fail-fast: false
matrix:
include:
- client: erigon
runs-on: [self-hosted, qa, Ethereum, rpc-performance]
- client: geth
runs-on: [self-hosted, qa, Ethereum, rpc-historical]
runs-on: ${{ matrix.runs-on }}
env:
RUN_ID: ${{ needs.setup.outputs.run_id }}
NETWORK: ${{ needs.setup.outputs.network }}
ERIGON_REFERENCE_DIR: /opt/erigon-versions/reference-version
ERIGON_REFERENCE_DATA_DIR: /opt/erigon-versions/reference-version/datadir
ERIGON_TESTBED_AREA: /opt/erigon-testbed
ERIGON_QA_PATH: /home/qarunner/erigon-qa
GETH_INSTALL_DIR: /opt/go-ethereum
CLIENT: ${{ matrix.client }}
steps:
- name: Checkout Erigon repository
if: matrix.client == 'erigon'
uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: "0"
- name: Checkout RPC Tests Repository
if: matrix.client == 'erigon' || needs.setup.outputs.run_geth == 'true'
run: |
rm -rf ${{runner.workspace}}/rpc-tests
git -c advice.detachedHead=false clone --depth 1 --branch v1.115.0 https://github.com/erigontech/rpc-tests ${{runner.workspace}}/rpc-tests
cd ${{runner.workspace}}/rpc-tests
- name: Clean Erigon Build Directory
if: matrix.client == 'erigon'
run: |
make clean
- name: Build Erigon RPCDaemon
if: matrix.client == 'erigon'
run: |
make rpcdaemon
working-directory: ${{ github.workspace }}
- name: Pause the Erigon instance dedicated to db maintenance
if: matrix.client == 'erigon'
run: |
python3 $ERIGON_QA_PATH/test_system/db-producer/pause_production.py || true
- name: Save Erigon Chaindata Directory
if: matrix.client == 'erigon'
id: save_chaindata_step
run: |
rm -rf $ERIGON_TESTBED_AREA/chaindata-prev || true
echo "Backup chaindata"
cp -r $ERIGON_REFERENCE_DATA_DIR/chaindata $ERIGON_TESTBED_AREA/chaindata-prev
rm -f $ERIGON_REFERENCE_DATA_DIR/logs/rpcdaemon.log || true
echo "datadir_saved=true" >> $GITHUB_OUTPUT
- name: Run RpcDaemon
id: rpcdaemon_running_step
if: matrix.client == 'erigon'
working-directory: ${{ github.workspace }}/build/bin
run: |
echo "Starting RpcDaemon..."
./rpcdaemon --datadir $ERIGON_REFERENCE_DATA_DIR --http.api admin,debug,eth,parity,erigon,trace,web3,txpool,ots,net --ws > erigon.log 2>&1 &
RPC_DAEMON_PID=$!
RPC_DAEMON_EXIT_STATUS=$?
echo "RPC_DAEMON_PID=$RPC_DAEMON_PID" >> $GITHUB_ENV
echo "rpc_daemon_started=true" >> $GITHUB_OUTPUT
sleep 5
tail erigon.log
if [ $RPC_DAEMON_EXIT_STATUS -ne 0 ]; then
echo "RpcDaemon failed to start"
echo "::error::Error detected during tests: RpcDaemon failed to start"
exit 1
fi
echo "RpcDaemon started"
- name: Wait for port 8545 to be opened
if: matrix.client == 'erigon' || needs.setup.outputs.run_geth == 'true'
run: |
# Geth is expected to be pre-running for the rpc-latest-geth runner
# Erigon was started above
for i in {1..30}; do
if nc -z localhost 8545; then
echo "Port 8545 is open"
break
fi
echo "Waiting for port 8545 to open..."
sleep 10
done
if ! nc -z localhost 8545; then
echo "Port 8545 did not open in time"
echo "::error::Error detected during tests: Port 8545 did not open in time"
exit 1
fi
- name: Run RPC Performance Tests
id: test_step
if: matrix.client == 'erigon' || needs.setup.outputs.run_geth == 'true'
run: |
set +e # Disable exit on error
failed_test=0
if [ "${{ matrix.client }}" == "erigon" ]; then
commit=$(git -C ${{runner.workspace}}/erigon rev-parse --short HEAD)
else
commit=$(git -C $GETH_INSTALL_DIR rev-parse --short HEAD)
fi
# Prepare historical test results directory
# a) Save text results to a directory with timestamp and commit hash
past_test_dir=$RPC_PAST_TEST_DIR/${CHAIN}_$(date +%Y%m%d_%H%M%S)_comp_perf_$commit
echo "past_test_dir=$past_test_dir" >> $GITHUB_ENV
mkdir -p $past_test_dir
# b) Save binary results to a fixed directory
bin_past_test_dir=$RPC_PAST_TEST_DIR/${CHAIN}_bin
rm -rf $bin_past_test_dir # we want only the latest binary files
mkdir -p $bin_past_test_dir
run_perf () {
workspace=$1
network=$2
method=$3
pattern=$4
sequence=$5
client=$6
result_dir=$workspace/rpc-tests/perf/reports/$network
result_file=$client-$method-result.json
# clean temporary area
cd $workspace/rpc-tests/perf
rm -rf ./reports/$network
python3 ./run_perf_tests.py --blockchain "$network" \
--test-type "$method" \
--pattern-file pattern/"$network"/"$pattern".tar \
--test-sequence "$sequence" \
--repetitions 5 \
--erigon-dir "/" \
--silk-dir "/" \
--test-mode 2 \
--test-report \
--json-report $result_dir/$result_file \
--testing-daemon $client
# Capture test runner script exit status
perf_exit_status=$?
# Detect the pre-built db version
if [ "$client" == "erigon" ]; then
db_version=$(python3 $ERIGON_QA_PATH/test_system/qa-tests/uploads/prod_info.py $ERIGON_REFERENCE_DIR/production.ini production erigon_repo_commit)
else
db_version=$commit
fi
# Check test runner script exit status
if [ $perf_exit_status -eq 0 ]; then
outcome=success
# Save all vegeta binary reports
echo "Save current vegeta binary files"
if [ -d "$workspace/rpc-tests/perf/reports/bin" ]; then
cp -r "$workspace/rpc-tests/perf/reports/bin" "$bin_past_test_dir"
else
echo "::warning::vegeta binary reports directory '$workspace/rpc-tests/perf/reports/bin' not found; skipping copy."
fi
echo "Execute Latency Percentile HDR Analysis"
cd $result_dir
python3 $ERIGON_QA_PATH/test_system/qa-tests/rpc-tests/perf_hdr_analysis.py \
--test_name $client-$method \
--input_file ./$result_file \
--output_file ./$client-$method-latency_hdr_analysis.pdf
perf_hdr_status=$?
if [ $perf_hdr_status -ne 0 ]; then
echo "::warning::perf_hdr_analysis.py failed with exit code $perf_hdr_status"
fi
else
failed_test=1
outcome=failure
fi
# Save results on DB
echo "Save test result on DB"
if [ "$client" == "erigon" ]; then
branch_name=${{ github.ref_name }}
commit_hash=$(git -C $workspace/erigon rev-parse HEAD)
else
branch_name="release"
commit_hash=$commit
fi
echo branch_name=$branch_name
echo commit_hash=$commit_hash
echo method=$method
echo db_version=$db_version
echo outcome=$outcome
echo result_file=$result_file
python3 $ERIGON_QA_PATH/test_system/qa-tests/uploads/upload_test_results.py \
--repo $client \
--branch $branch_name \
--commit $commit_hash \
--test_name rpc-performance-test-$client-$method \
--chain $CHAIN \
--runner ${{ runner.name }} \
--db_version $db_version \
--outcome $outcome \
--result_file $result_dir/$result_file
if [ $? -ne 0 ]; then
failed_test=1
echo "Failure saving test results on DB"
fi
# Save test results to a directory with timestamp and commit hash
if [ -d "$result_dir" ]; then
cp -r "$result_dir" "$past_test_dir"
else
echo "::warning::Result directory '$result_dir' does not exist; skipping archival to $past_test_dir"
fi
}
# Launch the RPC performance test runner
run_perf ${{runner.workspace}} $CHAIN eth_call stress_test_eth_call_001_14M 1:1,100:30,1000:20,10000:20,20000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN eth_getLogs stress_test_eth_getLogs_15M 1:1,100:30,1000:20,10000:20,20000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN eth_getBalance stress_test_eth_getBalance_15M 1:1,100:30,1000:20,10000:20,20000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN eth_getBlockByHash stress_test_eth_getBlockByHash_14M 1:1,100:30,1000:20,5000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN eth_getBlockByNumber stress_test_eth_getBlockByNumber_13M 1:1,100:30,1000:20,5000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN eth_getTransactionByHash stress_test_eth_getTransactionByHash_13M 1:1,100:30,1000:20,10000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN eth_getTransactionReceipt stress_test_eth_getTransactionReceipt_14M 1:1,100:30,1000:20,5000:20,10000:20,20000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN eth_createAccessList stress_test_eth_createAccessList_16M 1:1,100:30,1000:20,10000:20,20000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN debug_traceTransaction stress_test_debug_trace_transaction_21M 1:1,100:30,1000:20,5000:20 $CLIENT
run_perf ${{runner.workspace}} $CHAIN debug_storageRangeAt stress_test_debug_storageRangeAt_20M 1:1,100:30,1000:20,5000:20 $CLIENT
# Save the subsection reached status
echo "test_executed=true" >> $GITHUB_OUTPUT
if [ $failed_test -eq 0 ]; then
echo "TEST_RESULT=success" >> "$GITHUB_OUTPUT"
echo "Tests completed successfully"
else
echo "TEST_RESULT=failure" >> "$GITHUB_OUTPUT"
echo "Error detected during tests"
echo "::error::Error detected during tests"
exit 1
fi
- name: Stop Erigon RpcDaemon
if: always() && matrix.client == 'erigon'
working-directory: ${{ github.workspace }}/build/bin
run: |
# Clean up rpcdaemon process if it's still running
if [ -n "$RPC_DAEMON_PID" ] && kill -0 $RPC_DAEMON_PID 2> /dev/null; then
echo "RpcDaemon stopping..."
kill $RPC_DAEMON_PID
echo "RpcDaemon stopped"
else
echo "RpcDaemon has already terminated"
fi
- name: Upload RpcDaemon logs
if: matrix.client == 'erigon' && steps.test_step.outputs.test_executed == 'true'
uses: actions/upload-artifact@v6
with:
name: rpcdaemon-logs
path: $ERIGON_REFERENCE_DATA_DIR/logs/rpcdaemon.log
- name: Restore Erigon Chaindata Directory
if: always() && matrix.client == 'erigon' && steps.save_chaindata_step.outputs.datadir_saved == 'true'
run: |
if [ -d "$ERIGON_TESTBED_AREA/chaindata-prev" ] && [ "${{ steps.save_chaindata_step.outcome }}" == "success" ]; then
rm -rf $ERIGON_REFERENCE_DATA_DIR/chaindata
echo "Restore chaindata"
mv $ERIGON_TESTBED_AREA/chaindata-prev $ERIGON_REFERENCE_DATA_DIR/chaindata
fi
- name: Resume the Erigon instance dedicated to db maintenance
if: always() && matrix.client == 'erigon'
run: |
python3 $ERIGON_QA_PATH/test_system/db-producer/resume_production.py || true
- name: Run change point analysis
if: matrix.client == 'erigon' && steps.test_step.outputs.TEST_RESULT == 'success'
working-directory: ${{runner.workspace}}/rpc-tests/perf/reports/mainnet
run: |
set +e # Disable exit on error
open_change_points=0
python3 $ERIGON_QA_PATH/test_system/qa-tests/change-points/change_point_analysis.py --repo erigon
open_change_points=$?
cp change_point_analysis.pdf $past_test_dir
if [ $open_change_points -ne 0 ]; then
echo "Change point analysis found points that need to be investigated"
echo "::warning title=Change Point Analysis::Found change points that need to be investigated"
#echo "TEST_RESULT=failure" >> "$GITHUB_OUTPUT" -- enable in the future
fi
- name: Upload test results
if: (matrix.client == 'erigon' || needs.setup.outputs.run_geth == 'true') && steps.test_step.outputs.test_executed == 'true'
uses: actions/upload-artifact@v6
with:
name: test-results-${{ env.CHAIN }}-${{ matrix.client }}
path: ${{ env.past_test_dir }}
- name: Action to check failure condition
if: failure()
run: |
if [ "${{ steps.test_step.outputs.test_executed }}" != "true" ]; then
echo "::error::Test not executed, workflow failed for infrastructure reasons"
fi
exit 1
- name: Action for Success
if: steps.test_step.outputs.TEST_RESULT == 'success'
run: echo "::notice::Tests completed successfully"