Skip to content

Commit

Permalink
Run k6 against local Nuxt on PRs (#4924)
Browse files Browse the repository at this point in the history
* Run k6 against local Nuxt on PRs

* Use sample of real query terms

Remove reliance on system dictionary which may not be available

* Write text summary during execution

* Use talkback to prevent throttling of requests in CI

* Make the tapes smaller (#4985)

* Try fix just recipe bad substitution

* Use locales available in test for load testing

---------

Co-authored-by: Olga Bulat <[email protected]>
  • Loading branch information
sarayourfriend and obulat authored Sep 25, 2024
1 parent af8b89f commit d3553f2
Show file tree
Hide file tree
Showing 61 changed files with 63,028 additions and 35 deletions.
92 changes: 92 additions & 0 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,86 @@ jobs:
env:
DEPLOYMENT_ENV: production

nuxt-load-test:
name: Load test local frontend
runs-on: ubuntu-latest
needs:
- nuxt-build

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup CI env
uses: ./.github/actions/setup-env
with:
setup_python: false
install_recipe: node-install
locales: "test"

- name: Build and run Nuxt and Talkback
run: |
just frontend/run build
just frontend/run talkback &
env NUXT_PUBLIC_API_URL=http://127.0.0.1:49153/ just frontend/run start &
- name: Setup k6
uses: grafana/setup-k6-action@v1

- name: Wait for local Talkback to be available
# 10 seconds, talkback has a slow startup; this step will fail if Talkback never becomes available
run: npx wait-port -t 10000 :49153

- name: Wait for local Nuxt to be available
# 2 seconds, this step will fail if Nuxt never becomes available
run: npx wait-port -t 2000 http://127.0.0.1:8443/healthcheck

- name: Run k6 frontend all against local Nuxt
run: |
just k6 frontend all \
-e FRONTEND_URL=http://127.0.0.1:8443/ \
-e text_summary=/tmp/k6-summary.txt
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: k6-output
path: /tmp/k6-summary.txt

- name: Make comment body
shell: python
run: |
from pathlib import Path
summary = Path("/tmp/k6-summary.txt").read_text()
Path("/tmp/k6-summary-comment.txt").write_text(f"""
## Latest k6 run output[^update]
```
${summary}
```
[^update]: This comment will automatically update with new output each time k6 runs for this PR
""")
- uses: peter-evans/find-comment@v3
id: k6-summary-comment
with:
issue-number: ${{ github.event.pull_request.number }}
body-includes: Latest k6 run output

- name: Post comment summary
uses: peter-evans/create-or-update-comment@v4
# Do not comment on forks
if: |
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.owner.login == 'WordPress' &&
github.actor != 'dependabot[bot]'
with:
issue-number: ${{ github.event.pull_request.number }}
edit-mode: replace
body-path: /tmp/k6-summary-comment.txt
comment-id: ${{ steps.k6-summary-comment.outputs.comment-id }}

nuxt-checks:
name: Run Nuxt checks
if: |
Expand Down Expand Up @@ -1182,6 +1262,18 @@ jobs:
wait_time: 60 # check every minute
max_time: 1800 # allow up to 30 minutes for a deployment

- name: Setup k6
uses: grafana/setup-k6-action@v1

- name: Run k6 script
env:
K6_CLOUD_TOKEN: ${{ secrets.GC_K6_TOKEN }}
K6_SIGNING_SECRET: ${{ secrets.K6_SIGNING_SECRET }}
run: |
just k6 frontend all --out cloud \
-e FRONTEND_URL=https://staging.openverse.org/ \
-e signing_secret="$K6_SIGNING_SECRET"
deploy-api:
name: Deploy staging API
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions docker/dev_env/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ ENV PATH="${PNPM_BIN}:${N_PREFIX}/bin:${PDM_PYTHONS}:${HOME}/.local/bin:${PATH}"
# - nodejs: language runtime (includes npm but not Corepack)
# - docker*: used to interact with host Docker socket
# - postgresql*: required to connect to PostgreSQL databases
# - k6, words: used for load testing
# - k6: used for load testing
#
# pipx dependencies:
# - httpie: CLI HTTP client
Expand Down Expand Up @@ -65,7 +65,7 @@ RUN mkdir /pipx \
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \
unzip \
postgresql postgresql-devel \
k6 words \
k6 \
&& dnf clean all \
&& pipx install --global \
httpie \
Expand Down
34 changes: 32 additions & 2 deletions frontend/test/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const port = 49153
const host = "https://api.openverse.org"

const urlPatterns = {
search: /\/(?<mediaType>images|audio|video|model-3d)\/*\?(?<query>[\w&=+]+)/,
search: /\/(?<mediaType>images|audio|video|model-3d)\/*\?(?<query>.*?)$/u,
thumb:
/\/(?<mediaType>images|audio|video|model-3d)\/(?<uuid>[\w-]{32,})\/thumb/,
related:
Expand Down Expand Up @@ -100,6 +100,8 @@ const getBodyUtil = (tape) =>
tape.res?.headers["content-encoding"]?.includes(key)
)?.[1] ?? BodyUtils.default

const MAX_PEAKS = 200

/**
* Transform any response values to use the talkback
* proxy instead of pointing directly upstream for
Expand Down Expand Up @@ -142,11 +144,34 @@ const tapeDecorator = (tape) => {
const bodyUtil = getBodyUtil(tape)
const responseBody = bodyUtil.read(tape.res.body).toString()

const fixedResponseBody = responseBody.replace(
let fixedResponseBody = responseBody.replace(
/https?:\/\/api.openverse.org/g,
`http://localhost:${port}`
)

if (
tape.req.url.includes("/audio/") &&
!tape.req.url.includes("/audio/stats")
) {
const responseBodyJson = JSON.parse(fixedResponseBody)

// The search or related requests
if (responseBodyJson.results) {
responseBodyJson.results.map((result) => {
if (result.peaks && result.peaks.length > MAX_PEAKS) {
result.peaks = result.peaks.slice(0, MAX_PEAKS)
}
})
// The single result requests
} else if (
responseBodyJson.peaks &&
responseBodyJson.peaks.length > MAX_PEAKS
) {
responseBodyJson.peaks = responseBodyJson.peaks.slice(0, MAX_PEAKS)
}
fixedResponseBody = JSON.stringify(responseBodyJson)
}

tape.res.body = Buffer.from(bodyUtil.save(fixedResponseBody))
return tape
}
Expand All @@ -164,6 +189,11 @@ const opts = /** @type {Partial<TalkbackOptions>} */ ({
summary: false,
tapeNameGenerator,
tapeDecorator,
responseDecorator: (tape, req, context) => {
// Log responses to make debugging easier
console.log(req.method, req.url, tape.res?.status, context.id)
return tape
},
})

const server = talkback(opts)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
meta: {
createdAt: '2024-09-20T02:47:13.176Z',
host: 'https://api.openverse.org',
resHumanReadable: true,
resUncompressed: true,
},
req: {
headers: {
connection: 'keep-alive',
},
url: '/v1/audio/?q=Dom+Quixote+de+la+Mancha&license=by&extension=mp3&source=jamendo&peaks=true',
method: 'GET',
body: '',
},
res: {
status: 200,
headers: {
date: [
'Fri, 20 Sep 2024 02:47:13 GMT',
],
'content-type': [
'application/json',
],
'transfer-encoding': [
'chunked',
],
connection: [
'keep-alive',
],
vary: [
'Accept, Authorization, origin, Accept-Encoding',
],
allow: [
'GET, HEAD, OPTIONS',
],
'x-ratelimit-limit-anon_burst': [
'20/min',
],
'x-ratelimit-available-anon_burst': [
'17',
],
'x-ratelimit-limit-anon_sustained': [
'200/day',
],
'x-ratelimit-available-anon_sustained': [
'166',
],
'x-frame-options': [
'DENY',
],
'x-content-type-options': [
'nosniff',
],
'referrer-policy': [
'same-origin',
],
'cross-origin-opener-policy': [
'same-origin',
],
'x-request-id': [
'f03692b6-0328-4603-9737-771e9100765e',
],
'cf-cache-status': [
'HIT',
],
age: [
'1144',
],
'last-modified': [
'Fri, 20 Sep 2024 02:28:09 GMT',
],
server: [
'cloudflare',
],
'cf-ray': [
'8c5e7d536b31274b-ADL',
],
'content-encoding': [
'br',
],
},
body: {
result_count: 0,
page_count: 0,
page_size: 20,
page: 1,
results: [],
},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
meta: {
createdAt: '2024-09-20T02:47:12.052Z',
host: 'https://api.openverse.org',
resHumanReadable: true,
resUncompressed: true,
},
req: {
headers: {
connection: 'keep-alive',
},
url: '/v1/audio/?q=Dom+Quixote+de+la+Mancha&peaks=true',
method: 'GET',
body: '',
},
res: {
status: 200,
headers: {
date: [
'Fri, 20 Sep 2024 02:47:12 GMT',
],
'content-type': [
'application/json',
],
'transfer-encoding': [
'chunked',
],
connection: [
'keep-alive',
],
vary: [
'Accept, Authorization, origin, Accept-Encoding',
],
allow: [
'GET, HEAD, OPTIONS',
],
'x-ratelimit-limit-anon_burst': [
'20/min',
],
'x-ratelimit-available-anon_burst': [
'11',
],
'x-ratelimit-limit-anon_sustained': [
'200/day',
],
'x-ratelimit-available-anon_sustained': [
'6',
],
'x-frame-options': [
'DENY',
],
'x-content-type-options': [
'nosniff',
],
'referrer-policy': [
'same-origin',
],
'cross-origin-opener-policy': [
'same-origin',
],
'x-request-id': [
'43ea964c-6a59-4abb-8b12-8110e850d8fc',
],
'cf-cache-status': [
'HIT',
],
age: [
'1143',
],
'last-modified': [
'Fri, 20 Sep 2024 02:28:09 GMT',
],
server: [
'cloudflare',
],
'cf-ray': [
'8c5e7d4c599b274b-ADL',
],
'content-encoding': [
'br',
],
},
body: {
result_count: 0,
page_count: 0,
page_size: 20,
page: 1,
results: [],
},
},
}
Loading

0 comments on commit d3553f2

Please sign in to comment.