diff --git a/Makefile b/Makefile index e35d683d30..87fbc460f0 100644 --- a/Makefile +++ b/Makefile @@ -284,6 +284,17 @@ ci-pending-migrations: ci--meta-debug ######################### 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 + mv k6 tests/benchmark/k6 + +benchmark-fixtures-create: + ./tests/benchmark/fixtures.py create + +benchmark-run: + ./tests/benchmark/run.sh + +benchmark-fixtures-delete: + ./tests/benchmark/fixtures.py delete + +benchmark: benchmark-install benchmark-fixtures-create benchmark-run benchmark-fixtures-delete diff --git a/tests/benchmark/create_users.py b/tests/benchmark/create_users.py deleted file mode 100755 index bc1858015b..0000000000 --- a/tests/benchmark/create_users.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/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 deleted file mode 100755 index 743d0764b0..0000000000 --- a/tests/benchmark/delete_users.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/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/fixtures.py b/tests/benchmark/fixtures.py new file mode 100755 index 0000000000..48a2fe9200 --- /dev/null +++ b/tests/benchmark/fixtures.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import sys +from os import environ +from uuid import uuid4 + +import django + +environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings") +environ.setdefault("AUTHENTIK_BOOTSTRAP_PASSWORD", "akadmin") +environ.setdefault("AUTHENTIK_BOOTSTRAP_TOKEN", "akadmin") +environ.setdefault("AUTHENTIK_BOOTSTRAP_EMAIL", "akadmin@authentik.test") +django.setup() + +from django.conf import settings + +from authentik.core.models import Group, User +from authentik.tenants.models import Domain, Tenant + +settings.CELERY["task_always_eager"] = True + + +def user_list(): + # Number of users, groups per user, parents per groups + tenants = [ + (10, 0, 0), + (100, 0, 0), + (1000, 0, 0), + (10000, 0, 0), + (10, 20, 0), + (100, 20, 0), + (1000, 20, 0), + (10000, 20, 0), + (10, 20, 10), + (100, 20, 10), + (1000, 20, 10), + (10000, 20, 10), + ] + + for tenant in tenants: + user_count = tenant[0] + groups_per_user = tenant[1] + parents_per_group = tenant[2] + tenant_name = f"user-list-{user_count}-{groups_per_user}-{parents_per_group}" + + t = Tenant.objects.create(schema_name=f"t_{tenant_name.replace('-', '_')}", name=uuid4()) + Domain.objects.create(tenant=t, domain=f"{tenant_name}.localhost") + + with t: + for _ in range(user_count): + User.objects.create(username=uuid4(), name=uuid4()) + for user in User.objects.exclude_anonymous().exclude(username="akadmin"): + for _ in range(groups_per_user): + user.ak_groups.add(Group.objects.create(name=uuid4())) + for group in Group.objects.exclude(name="authentik Admins"): + for _ in range(parents_per_group): + new_group = Group.objects.create(name=uuid4()) + group.parent = new_group + group.save() + group = new_group + + +def delete(): + Tenant.objects.exclude(schema_name="public").delete() + + +if __name__ == "__main__": + if len(sys.argv) < 2: + action = "create" + else: + action = sys.argv[1] + + match action: + case "create": + user_list() + case "delete": + delete() + case _: + print("Unknown action. Should be create or delete") + exit(1) diff --git a/tests/benchmark/init.py b/tests/benchmark/init.py deleted file mode 100644 index 67e845cd1b..0000000000 --- a/tests/benchmark/init.py +++ /dev/null @@ -1,8 +0,0 @@ -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/user_list.js b/tests/benchmark/user_list.js old mode 100755 new mode 100644 index 6a802d46eb..a42f8854b4 --- a/tests/benchmark/user_list.js +++ b/tests/benchmark/user_list.js @@ -7,51 +7,59 @@ export const options = { discardResponseBodies: true, scenarios: Object.fromEntries( [ - [10, 20], - [100, 20], - [1000, 20], - [10000, 20], - [10, 100], - [100, 100], - [1000, 100], - [10000, 100], + // Number of users, number of groups per user, number of parents per group, page size + [10, 0, 0, 20], + [100, 0, 0, 20], + [1000, 0, 0, 20], + [10000, 0, 0, 20], + [10, 0, 0, 100], + [100, 0, 0, 100], + [1000, 0, 0, 100], + [10000, 0, 0, 100], + [10, 20, 0, 20], + [100, 20, 0, 20], + [1000, 20, 0, 20], + [10000, 20, 0, 20], + [10, 20, 10, 20], + [100, 20, 10, 20], + [1000, 20, 10, 20], + [10000, 20, 10, 20], ].map((obj, i) => [ - `${obj[0]}_${obj[1]}`, + `${obj[0]}_${obj[1]}_${obj[2]}_${obj[3]}`, { executor: "constant-vus", vus: 10, - duration: "10s", - startTime: `${10 * i}s`, + duration: "30s", + startTime: `${30 * i}s`, env: { USER_COUNT: `${obj[0]}`, - PAGE_SIZE: `${obj[1]}`, + GROUPS_PER_USER: `${obj[1]}`, + PARENTS_PER_GROUP: `${obj[2]}`, + PAGE_SIZE: `${obj[3]}`, }, tags: { user_count: `${obj[0]}`, - page_size: `${obj[1]}`, + groups_per_user: `${obj[1]}`, + parents_per_group: `${obj[2]}`, + page_size: `${obj[3]}`, }, }, ]), ), }; -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 groups_per_user = Number(__ENV.GROUPS_PER_USER); + const parents_per_group = Number(__ENV.PARENTS_PER_GROUP); + const domain = `user-list-${user_count}-${groups_per_user}-${parents_per_group}`; const page_size = Number(__ENV.PAGE_SIZE); - const pages = Math.round(user_count / 20); + const pages = Math.round(user_count / page_size); 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}`, + `http://${domain}.localhost:9000/api/v3/core/users/?page=${page}&page_size=${page_size}`, null, { headers: {