Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: GraphQL performance tests continuation #1302

Merged
merged 12 commits into from
Dec 14, 2023
45 changes: 38 additions & 7 deletions .github/workflows/performance-tests.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,48 @@
name: Performance tests
name: GraphQL performance tests

on: workflow_dispatch

jobs:
compile:
name: Compile site assets
run_tests:
name: Run tests
runs-on: ubuntu-latest
steps:
- name: Check out the repo
- name: Check out
uses: actions/checkout@v2
- name: Run the build process with Docker
- name: Run k6 and upload results to Prometheus
uses: addnab/docker-run-action@v3
with:
image: grafana/k6:latest
options: -rm -e K6_PROMETHEUS_RW_USERNAME=${{ secrets.K6_PROMETHEUS_RW_USERNAME }} -e K6_PROMETHEUS_RW_PASSWORD=${{ secrets.K6_PROMETHEUS_RW_PASSWORD }} -e K6_PROMETHEUS_RW_SERVER_URL=${{ secrets.K6_PROMETHEUS_RW_SERVER_URL }}
run: k6 run -o experimental-prometheus-rw --tag testid=StateAccounts_Office/4/ --env ENV=test --env CUBE=StateAccounts_Office/4/ - <k6/performance-tests/graphql/metadata.js && k6 run -o experimental-prometheus-rw --tag testid=StateAccounts_Office/4/ --env ENV=int --env CUBE=StateAccounts_Office/4/ - <k6/performance-tests/graphql/metadata.js && k6 run -o experimental-prometheus-rw --tag testid=StateAccounts_Office/4/ --env ENV=prod --env CUBE=StateAccounts_Office/4/ - <k6/performance-tests/graphql/metadata.js
options: |
-v ${{ github.workspace }}:/root
-e K6_PROMETHEUS_RW_USERNAME=${{ secrets.K6_PROMETHEUS_RW_USERNAME }}
-e K6_PROMETHEUS_RW_PASSWORD=${{ secrets.K6_PROMETHEUS_RW_PASSWORD }}
-e K6_PROMETHEUS_RW_SERVER_URL=${{ secrets.K6_PROMETHEUS_RW_SERVER_URL }}
run: |
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=test --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=test --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=test --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=test --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=test --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=test --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=test --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubePreview.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=test --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubePreview.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=test --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubePreview.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=int --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=int --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=int --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=int --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=int --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=int --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=int --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubePreview.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=int --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubePreview.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=int --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubePreview.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=prod --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=prod --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeComponents --env ENV=prod --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubeComponents.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=prod --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=prod --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubeMetadata --env ENV=prod --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubeMetadata.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=prod --env CUBE_IRI=https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9 --env CUBE_LABEL=Photovoltaikanlagen/9 - </root/k6/performance-tests/graphql/DataCubePreview.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=prod --env CUBE_IRI=https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2 --env CUBE_LABEL=NFI/2023-2 - </root/k6/performance-tests/graphql/DataCubePreview.js &&
k6 run -o experimental-prometheus-rw --tag testid=DataCubePreview --env ENV=prod --env CUBE_IRI=https://energy.ld.admin.ch/elcom/electricityprice --env CUBE_LABEL=Elcom - </root/k6/performance-tests/graphql/DataCubePreview.js
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ Playwright is run on every successful deployment of a branch. Screenshots are ma
A special [test page](http://localhost:3000/en/__test) shows all the charts that are screenshotted.
Those charts configurations are kept in the repository.

## GraphQL performance tests

The project uses a combination of [k6](https://k6.io) and [Grafana](https://grafana.com) with [Prometheus](https://k6.io/docs/results-output/real-time/prometheus-remote-write/) for GraphQL performance testing.

### Automation

To ensure constant monitoring of the performance of selected GraphQL queries, the performance tests are run [once an hour](https://github.com/visualize-admin/visualization-tool/blob/main/.github/workflows/performance-tests.yml) against each environment of the application. The results are then sent to the governmental Grafana dashboards.

### How to add or modify the tests

To add or modify the performance tests, go to the [k6/performance-tests](https://github.com/visualize-admin/visualization-tool/tree/main/k6/performance-tests) folder. The GitHub Action is generated by running the `yarn run github:codegen` command; be sure to run it after modifying the [generate-github-action.js](`https://github.com/visualize-admin/visualization-tool/blob/main/k6/performance-tests/generate-github-action.js`) file.

## Load tests

The project uses [k6](https://k6.io) for load testing.
Expand Down
58 changes: 40 additions & 18 deletions k6/performance-tests/generate-github-action.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,59 @@
const fs = require("fs");

const envs = ["test", "int", "prod"];
const queries = ["metadata"];
const cubes = ["StateAccounts_Office/4/"];
const queries = ["DataCubeComponents", "DataCubeMetadata", "DataCubePreview"];
const cubes = [
{
iri: "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/9",
label: "Photovoltaikanlagen/9",
},
{
iri: "https://environment.ld.admin.ch/foen/nfi/nfi_C-96/cube/2023-2",
label: "NFI/2023-2",
},
{
iri: "https://energy.ld.admin.ch/elcom/electricityprice",
label: "Elcom",
},
];
const commands = envs
.flatMap((e) =>
queries.flatMap((q) => cubes.map((c) => getRunCommand(e, q, c)))
)
.join(" &&\n ");

const generate = () => {
const file = `name: Performance tests
const file = `name: GraphQL performance tests

on: workflow_dispatch
on:
workflow_dispatch:
schedule:
- cron: 37 * * * *"

jobs:
compile:
name: Compile site assets
run_tests:
name: Run tests
runs-on: ubuntu-latest
steps:
- name: Check out the repo
- name: Check out
uses: actions/checkout@v2
- name: Run the build process with Docker
- name: Run k6 and upload results to Prometheus
uses: addnab/docker-run-action@v3
with:
image: grafana/k6:latest
options: -rm -e K6_PROMETHEUS_RW_USERNAME=\${{ secrets.K6_PROMETHEUS_RW_USERNAME }} -e K6_PROMETHEUS_RW_PASSWORD=\${{ secrets.K6_PROMETHEUS_RW_PASSWORD }} -e K6_PROMETHEUS_RW_SERVER_URL=\${{ secrets.K6_PROMETHEUS_RW_SERVER_URL }}
run: ${envs
.map((env) => {
return queries.map((query) => {
return cubes.map((cube) => {
return `k6 run -o experimental-prometheus-rw --tag testid=${cube} --env ENV=${env} --env CUBE=${cube} - <k6/performance-tests/graphql/${query}.js`;
});
});
})
.join(" && ")}`;
options: |
-v \${{ github.workspace }}:/root
-e K6_PROMETHEUS_RW_USERNAME=\${{ secrets.K6_PROMETHEUS_RW_USERNAME }}
-e K6_PROMETHEUS_RW_PASSWORD=\${{ secrets.K6_PROMETHEUS_RW_PASSWORD }}
-e K6_PROMETHEUS_RW_SERVER_URL=\${{ secrets.K6_PROMETHEUS_RW_SERVER_URL }}
run: |
${commands}`;

fs.writeFileSync("./.github/workflows/performance-tests.yml", file);
};

generate();

function getRunCommand(env, query, cube) {
return `k6 run -o experimental-prometheus-rw --tag testid=${query} --env ENV=${env} --env CUBE_IRI=${cube.iri} --env CUBE_LABEL=${cube.label} - </root/k6/performance-tests/graphql/${query}.js`;
}
50 changes: 50 additions & 0 deletions k6/performance-tests/graphql/DataCubeComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import exec from "k6/execution";
import http from "k6/http";

const query = `query DataCubeComponents(
$sourceType: String!
$sourceUrl: String!
$locale: String!
$cubeFilter: DataCubeComponentFilter!
) {
dataCubeComponents(
sourceType: $sourceType
sourceUrl: $sourceUrl
locale: $locale
cubeFilter: $cubeFilter
)
}`;

const env = __ENV.ENV;
const cubeIri = __ENV.CUBE_IRI;
const cubeLabel = __ENV.CUBE_LABEL;

const variables = {
locale: "en",
sourceType: "sparql",
sourceUrl: "https://lindas.admin.ch/query",
cubeFilter: {
iri: cubeIri,
},
};

/** @type {import("k6/options").Options} */
export const options = {
iterations: 1,
};

const headers = {
"Content-Type": "application/json",
"x-visualize-cache-control": "no-cache",
};

export default function Components() {
exec.vu.metrics.tags.env = env;
exec.vu.metrics.tags.cube = cubeLabel;

http.post(
`https://${env === "prod" ? "" : `${env}.`}visualize.admin.ch/api/graphql`,
JSON.stringify({ query, variables }),
{ headers }
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,36 @@ const query = `query DataCubeMetadata(
)
}`;

const env = __ENV.ENV;
const cubeIri = __ENV.CUBE_IRI;
const cubeLabel = __ENV.CUBE_LABEL;

const variables = {
locale: "en",
sourceType: "sparql",
sourceUrl: "https://lindas.admin.ch/query",
cubeFilter: {
iri: "https://culture.ld.admin.ch/sfa/StateAccounts_Office/4/",
iri: cubeIri,
},
};

const env = __ENV.ENV;
const cube = __ENV.CUBE;

/** @type {import("k6/options").Options} */
export const options = {
iterations: 1,
};

const headers = {
"Content-Type": "application/json",
"x-visualize-cache-control": "no-cache",
};

export default function Components() {
exec.vu.metrics.tags.env = env;
exec.vu.metrics.tags.cube = cube;
exec.vu.metrics.tags.cube = cubeLabel;

http.post(
`https://${env === "prod" ? "" : `${env}.`}visualize.admin.ch/api/graphql`,
JSON.stringify({ query, variables }),
{
headers: {
"Content-Type": "application/json",
"x-visualize-cache-control": "no-cache",
},
}
{ headers }
);
}
67 changes: 67 additions & 0 deletions k6/performance-tests/graphql/DataCubePreview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import exec from "k6/execution";
import http from "k6/http";

const query = `query DataCubePreview(
$iri: String!
$sourceType: String!
$sourceUrl: String!
$locale: String!
$latest: Boolean
$disableValuesLoad: Boolean = true
) {
dataCubeByIri(
iri: $iri
sourceType: $sourceType
sourceUrl: $sourceUrl
locale: $locale
latest: $latest
disableValuesLoad: $disableValuesLoad
) {
iri
title
description
publicationStatus
observations(
sourceType: $sourceType
sourceUrl: $sourceUrl
preview: true
limit: 10
) {
data
sparqlEditorUrl
}
}
}`;

const env = __ENV.ENV;
const cubeIri = __ENV.CUBE_IRI;
const cubeLabel = __ENV.CUBE_LABEL;

const variables = {
iri: cubeIri,
sourceType: "sparql",
sourceUrl: "https://lindas.admin.ch/query",
locale: "en",
latest: false,
};

/** @type {import("k6/options").Options} */
export const options = {
iterations: 1,
};

const headers = {
"Content-Type": "application/json",
"x-visualize-cache-control": "no-cache",
};

export default function Components() {
exec.vu.metrics.tags.env = env;
exec.vu.metrics.tags.cube = cubeLabel;

http.post(
`https://${env === "prod" ? "" : `${env}.`}visualize.admin.ch/api/graphql`,
JSON.stringify({ query, variables }),
{ headers }
);
}
Loading