From a7cb808cadf06bbd48605c9ff48e401a07530157 Mon Sep 17 00:00:00 2001 From: Marc 'risson' Schmitt Date: Fri, 5 Apr 2024 05:32:40 +0200 Subject: [PATCH] init benchmarks Signed-off-by: Marc 'risson' Schmitt --- .gitignore | 6 +++ Makefile | 9 ++++ scripts/docker-compose.yml | 1 + tests/benchmark/__init__.py | 0 tests/benchmark/create_users.py | 9 ++++ tests/benchmark/delete_users.py | 7 ++++ tests/benchmark/init.py | 8 ++++ tests/benchmark/run.sh | 30 +++++++++++++ tests/benchmark/user_list.js | 74 +++++++++++++++++++++++++++++++++ 9 files changed, 144 insertions(+) create mode 100644 tests/benchmark/__init__.py create mode 100755 tests/benchmark/create_users.py create mode 100755 tests/benchmark/delete_users.py create mode 100644 tests/benchmark/init.py create mode 100755 tests/benchmark/run.sh create mode 100755 tests/benchmark/user_list.js diff --git a/.gitignore b/.gitignore index 713258ca75..599646f427 100644 --- a/.gitignore +++ b/.gitignore @@ -209,3 +209,9 @@ source_docs/ ### Golang ### /vendor/ + +### Benchmark ### +tests/benchmark/k6 +tests/benchmark/**/*.json +tests/benchmark/**/*.ndjson +tests/benchmark/**/*.html diff --git a/Makefile b/Makefile index 4cc73e0c8c..e35d683d30 100644 --- a/Makefile +++ b/Makefile @@ -278,3 +278,12 @@ ci-bandit: ci--meta-debug ci-pending-migrations: ci--meta-debug ak makemigrations --check + +######################### +## Benchmark +######################### + +benchmark-install: + cd tests/benchmark + go install go.k6.io/xk6/cmd/xk6@latest + $$(go env GOPATH)/bin/xk6 build latest --with github.com/grafana/xk6-exec@latest diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml index 7e269c5967..94aac4fc80 100644 --- a/scripts/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -4,6 +4,7 @@ services: postgresql: container_name: postgres image: docker.io/library/postgres:16 + command: "-c max_connections=500" volumes: - db-data:/var/lib/postgresql/data environment: diff --git a/tests/benchmark/__init__.py b/tests/benchmark/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/benchmark/create_users.py b/tests/benchmark/create_users.py new file mode 100755 index 0000000000..bc1858015b --- /dev/null +++ b/tests/benchmark/create_users.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +import sys +from uuid import uuid4 + +from tests.benchmark.init import * + +while User.objects.count() < int(sys.argv[1]): + User.objects.create(username=uuid4(), name=uuid4()) diff --git a/tests/benchmark/delete_users.py b/tests/benchmark/delete_users.py new file mode 100755 index 0000000000..743d0764b0 --- /dev/null +++ b/tests/benchmark/delete_users.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +from uuid import uuid4 + +from tests.benchmark.init import * + +User.objects.exclude_anonymous().exclude(username="akadmin").delete() diff --git a/tests/benchmark/init.py b/tests/benchmark/init.py new file mode 100644 index 0000000000..67e845cd1b --- /dev/null +++ b/tests/benchmark/init.py @@ -0,0 +1,8 @@ +from os import environ + +import django + +environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") +django.setup() + +from authentik.core.models import User diff --git a/tests/benchmark/run.sh b/tests/benchmark/run.sh new file mode 100755 index 0000000000..808822897e --- /dev/null +++ b/tests/benchmark/run.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -euo pipefail + +BASE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)" + +function _k6 { + local filename="${1}" + + "${BASE_DIR}/k6" \ + run \ + --out "web-dashboard=port=-1&report=${filename%.*}.html&period=1s&tag=name&tag=group&tag=user_count&tag=page_size" \ + --out "json=${filename%.*}.json" \ + "${@}" +} + +filename="" +if [ "${#}" -ge 1 ]; then + filename="${1:-}" + shift +fi + + +if [ -f "${filename}" ]; then + _k6 "${filename}" "${@}" +else + find "${BASE_DIR}" -name '*.js' | while read -r f; do + _k6 "${f}" "${@}" + done +fi diff --git a/tests/benchmark/user_list.js b/tests/benchmark/user_list.js new file mode 100755 index 0000000000..6a802d46eb --- /dev/null +++ b/tests/benchmark/user_list.js @@ -0,0 +1,74 @@ +import exec from "k6/execution"; +import { command } from "k6/x/exec"; +import http from "k6/http"; +import { check, group } from "k6"; + +export const options = { + discardResponseBodies: true, + scenarios: Object.fromEntries( + [ + [10, 20], + [100, 20], + [1000, 20], + [10000, 20], + [10, 100], + [100, 100], + [1000, 100], + [10000, 100], + ].map((obj, i) => [ + `${obj[0]}_${obj[1]}`, + { + executor: "constant-vus", + vus: 10, + duration: "10s", + startTime: `${10 * i}s`, + env: { + USER_COUNT: `${obj[0]}`, + PAGE_SIZE: `${obj[1]}`, + }, + tags: { + user_count: `${obj[0]}`, + page_size: `${obj[1]}`, + }, + }, + ]), + ), +}; + +export function setup() { + command("./create_users.py", ["11000"]); +} + +export function teardown() { + command("./delete_users.py"); +} + +export default function () { + const user_count = Number(__ENV.USER_COUNT); + const page_size = Number(__ENV.PAGE_SIZE); + const pages = Math.round(user_count / 20); + let requests = []; + for (let page = 1; page <= pages; page++) { + requests.push([ + "GET", + `http://localhost:9000/api/v3/core/users/?page=${page}&page_size=${page_size}`, + null, + { + headers: { + Authorization: "Bearer akadmin", + "Content-Type": "application/json", + Accept: "*/*", + }, + tags: { + name: "/core/users/", + }, + }, + ]); + } + const responses = http.batch(requests); + for (let page = 1; page <= pages; page++) { + check(responses[page - 1], { + "status is 200": (res) => res.status === 200, + }); + } +}