Skip to content

Commit 17652ce

Browse files
authored
Add performance testing workflow (#1086)
* Add performance testing workflow
1 parent c0634a3 commit 17652ce

File tree

4 files changed

+166
-0
lines changed

4 files changed

+166
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Performance testing
2+
3+
# Run when a PR comment is created (issues and PRs are considered the same entity in the GitHub API)
4+
on:
5+
issue_comment:
6+
types: [created]
7+
8+
# Add some extra perms to comment on a PR
9+
permissions:
10+
pull-requests: write
11+
contents: read
12+
13+
jobs:
14+
run-perftests:
15+
# Make sure 1. this is a PR, not an issue 2. it contains "/run performance test" anywhere in the body
16+
if: github.event.issue.pull_request && contains(github.event.comment.body, '/run performance test')
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Set up WireGuard
20+
uses: egor-tensin/[email protected]
21+
with:
22+
endpoint: '${{ secrets.WG_PERF_ENDPOINT }}'
23+
endpoint_public_key: '${{ secrets.WG_PERF_ENDPOINT_PUBLIC_KEY }}'
24+
ips: '${{ secrets.WG_PERF_IPS }}'
25+
allowed_ips: '${{ secrets.WG_PERF_ALLOWED_IPS }}'
26+
private_key: '${{ secrets.WG_PERF_PRIVATE_KEY }}'
27+
- name: Check out repository
28+
uses: actions/checkout@v3
29+
# Previous step checks out default branch, so we check out the pull request's branch
30+
- name: Switch to PR branch
31+
run: |
32+
hub pr checkout ${{ github.event.issue.number }}
33+
env:
34+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35+
- name: Set up repository # mimics install.sh in the README except that delphi is cloned from the PR rather than main
36+
run: |
37+
cd ..
38+
mkdir -p driver/repos/delphi
39+
cd driver/repos/delphi
40+
git clone https://github.com/cmu-delphi/operations
41+
git clone https://github.com/cmu-delphi/utils
42+
git clone https://github.com/cmu-delphi/flu-contest
43+
git clone https://github.com/cmu-delphi/nowcast
44+
cd ../../
45+
46+
cd ..
47+
cp -R delphi-epidata driver/repos/delphi/delphi-epidata
48+
cd -
49+
50+
ln -s repos/delphi/delphi-epidata/dev/local/Makefile
51+
- name: Build & run epidata
52+
run: |
53+
cd ../driver
54+
sudo make web sql="${{ secrets.DB_CONN_STRING }}"
55+
- name: Build & run Locust
56+
run: |
57+
cd ../driver/repos/delphi/delphi-epidata/tests/performance
58+
docker build -t locust .
59+
docker run --net=host -v $PWD:/mnt/locust -e CSV=/mnt/locust/v4-requests-smaller.csv locust -f /mnt/locust/v4.py --host http://127.0.0.1:8080/ --users 10 --spawn-rate 1 --headless -t 15m
60+
61+
comment-output:
62+
runs-on: ubuntu-latest
63+
if: success() || failure() # but not if skipped
64+
needs: run-perftests
65+
steps:
66+
- name: Comment run results
67+
# URL that links to run results
68+
env:
69+
GITHUB_WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
70+
uses: actions/github-script@v5
71+
with:
72+
github-token: ${{secrets.GITHUB_TOKEN}}
73+
script: |
74+
github.rest.issues.createComment({
75+
issue_number: context.issue.number,
76+
owner: context.repo.owner,
77+
repo: context.repo.repo,
78+
body: '✅ Performance tests complete! Click here to view results: ${{ env.GITHUB_WORKFLOW_URL }}'
79+
})
80+

tests/performance/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM locustio/locust:latest
2+
RUN pip3 install locust-plugins
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
epidata/covidcast/?signal=jhu-csse%3Aconfirmed_7dav_incidence_prop&geo=state%3Any&time=day%3A20210117-20210517&format=json&fields=value%2Cstderr%2Ctime_value
2+
epidata/covidcast/?signal=jhu-csse%3Aconfirmed_7dav_incidence_prop&geo=county%3A33011&time=day%3A20211102-20220302&format=json&fields=value%2Cstderr%2Ctime_value
3+
epidata/covidcast/?signal=quidel-covid-ag%3Acovid_ag_smoothed_pct_positive&geo=nation%3Aus&time=day%3A20200526-20220224&format=json&fields=value%2Cstderr%2Ctime_value
4+
epidata/covidcast/?signal=jhu-csse%3Aconfirmed_7dav_incidence_prop&geo=county%3A27079&time=day%3A20210906-20220106&format=json&fields=value%2Cstderr%2Ctime_value
5+
epidata/covidcast/?signal=jhu-csse%3Aconfirmed_7dav_incidence_prop&geo=county%3A34001%2C34003%2C34005%2C34007%2C34009%2C34011%2C34013%2C34015%2C34017%2C34019%2C34021%2C34023%2C34025%2C34027%2C34029%2C34031%2C34033%2C34035%2C34037%2C34039%2C34041%2C34000&time=day%3A20220301&format=json&fields=value%2Cstderr%2Cgeo_value
6+
epidata/covidcast/?signal=jhu-csse%3Aconfirmed_7dav_incidence_prop&geo=county%3A42051&time=day%3A20211107-20220307&format=json&fields=value%2Cstderr%2Ctime_value
7+
epidata/covidcast/trend?signal=fb-survey%3Asmoothed_whh_cmnty_cli&geo=county%3A36061&date=20220311&basis_shift=7&window=20211113-20220313&format=json&fields=-geo_type%2C-geo_value%2C-signal_signal%2C-signal_source
8+
epidata/covidcast/?signal=fb-survey%3Asmoothed_wwearing_mask_7d&geo=county%3A48021&time=day%3A20211016-20220216&format=json&fields=value%2Cstderr%2Ctime_value
9+
epidata/covidcast/trend?signal=fb-survey%3Asmoothed_wwearing_mask_7d%2Csmoothed_wcovid_vaccinated_appointment_or_accept%2Csmoothed_wcli%2Csmoothed_whh_cmnty_cli&signal=google-symptoms%3As05_smoothed_search%2Cs02_smoothed_search&signal=doctor-visits%3Asmoothed_adj_cli&signal=quidel-covid-ag%3Acovid_ag_smoothed_pct_positive&signal=jhu-csse%3Aconfirmed_7dav_incidence_prop%2Cdeaths_7dav_incidence_prop&signal=hhs%3Aconfirmed_admissions_covid_1d_prop_7dav&geo=county%3A36001&date=20210318&basis_shift=7&window=20210118-20210518&format=json&fields=-geo_type%2C-geo_value
10+
epidata/covidcast/?signal=fb-survey%3Asmoothed_wwearing_mask_7d&geo=county%3A36061&time=day%3A20211106-20220306&format=json&fields=value%2Cstderr%2Ctime_value

tests/performance/v4.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#
2+
# Run using Docker method like:
3+
#
4+
# 1. Build the container:
5+
#
6+
# docker build -t locust .
7+
#
8+
# 2. Run it, where ${CSV} is a CSV file with requests terminated by a newline.
9+
# A bit of a hacky thing is happening here, in which the `-i` ("iterations")
10+
# flag's value is set to the output of "$(cat ${CSV} | wc -l)". This
11+
# effectively runs locust for the same number of iterations as there are
12+
# lines in the CSV file. This seems to reliably use each line in the file
13+
# once, no matter if one or many locust clients have been spun up. There is
14+
# probably a nicer way to do this, though.
15+
#
16+
# You can fill in ${CSV} manually with the name of the file, or export it
17+
# into your shell env.
18+
#
19+
# The below example assumes you want to run against the prod API endpoint,
20+
# but just change it as you need. Goes for the other params as well (port,
21+
# --users, --spawn-rate, etc.).
22+
#
23+
# Ex. Prod API
24+
# ----
25+
# docker run -p 8089:8089 \
26+
# -v $PWD:/mnt/locust \
27+
# -e CSV=/mnt/locust/${CSV} \
28+
# locust -f /mnt/locust/v4.py \
29+
# --host https://api.covidcast.cmu.edu/ \
30+
# --users 10 \
31+
# --spawn-rate 1 \
32+
# --autostart \
33+
# -i "$(cat ${CSV} | wc -l)"
34+
#
35+
# Ex. QA v3
36+
# ----
37+
# docker run - p 8089: 8089 \
38+
# -v $PWD: / mnt/locust \
39+
# -e CSV = /mnt/locust /${CSV} \
40+
# locust - f / mnt/locust/v4.py \
41+
# --host https: // staging.delphi.cmu.edu/qa/epi3 / \
42+
# --users 10 \
43+
# --spawn-rate 1 \
44+
# --autostart \
45+
# -i "$(cat ${CSV} | wc -l)"
46+
#
47+
# Ex. QA v4
48+
# ----
49+
# docker run - p 8089: 8089 \
50+
# -v $PWD: / mnt/locust \
51+
# -e CSV = /mnt/locust /${CSV} \
52+
# locust - f / mnt/locust/v4.py \
53+
# --host https: // staging.delphi.cmu.edu/qa/epi4 / \
54+
# --users 10 \
55+
# --spawn-rate 1 \
56+
# --autostart \
57+
# -i "$(cat ${CSV} | wc -l)"
58+
#
59+
60+
import os
61+
from locust_plugins.csvreader import CSVReader
62+
from locust import HttpUser, task, between
63+
64+
csv = os.environ['CSV']
65+
session_reader = CSVReader(csv)
66+
67+
68+
class EpidataUser(HttpUser):
69+
wait_time = between(1, 1)
70+
71+
@task
72+
def epidata_request(self):
73+
url = next(session_reader)
74+
self.client.get(f"{url[0]}")

0 commit comments

Comments
 (0)