From 2bc7946bc830c725831252e083e0a237e569dfbd Mon Sep 17 00:00:00 2001 From: Daniel DeGroff Date: Tue, 28 Jan 2025 12:56:38 -0700 Subject: [PATCH 1/2] Use a virtual thread factory --- src/main/java/io/fusionauth/load/Foreman.java | 76 +++++++++---------- src/main/resources/Create-Applications.json | 2 +- src/main/resources/Create-Tenants.json | 2 +- src/main/resources/HTTP.json | 2 +- .../OAuth2-AuthorizationCodeGrant.json | 2 +- src/main/resources/User-Logins.json | 2 +- src/main/resources/User-Registrations.json | 2 +- src/main/resources/User-RetrieveEmail.json | 2 +- src/main/resources/User-Search.json | 2 +- src/main/resources/User-SearchData.json | 2 +- src/main/script/load-test.sh | 4 +- 11 files changed, 48 insertions(+), 50 deletions(-) diff --git a/src/main/java/io/fusionauth/load/Foreman.java b/src/main/java/io/fusionauth/load/Foreman.java index 39c4d09..8dbbc95 100644 --- a/src/main/java/io/fusionauth/load/Foreman.java +++ b/src/main/java/io/fusionauth/load/Foreman.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2022, FusionAuth, All Rights Reserved + * Copyright (c) 2012-2025, FusionAuth, All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,55 +47,53 @@ public class Foreman implements Buildable { public Foreman execute() throws InterruptedException { initialize(); - ExecutorService pool = Executors.newFixedThreadPool(workers.size()); - - // Gradually build up workers, to reduce the chance of failures while we get going. - for (Worker worker : workers) { - WorkerExecutor executor = new WorkerExecutor(worker, loopCount, listeners); - pool.execute(executor); - try { - Thread.sleep(1123); - } catch (Exception ignore) { + // Note that we are going to use virtual threads, so in theory we don't need a pool, but we are trying to simulate clients or workers + // so keep the thread pool and just use a virtual factory. + try (ExecutorService pool = Executors.newFixedThreadPool(workers.size())) { + + // Gradually build up workers, to reduce the chance of failures while we get going. + for (Worker worker : workers) { + WorkerExecutor executor = new WorkerExecutor(worker, loopCount, listeners); + pool.execute(executor); } - } - if (this.reporter != null) { - this.reporter.schedule(); - } + if (this.reporter != null) { + this.reporter.schedule(); + } - pool.shutdown(); - pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + pool.shutdown(); + pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); - listeners.forEach(SampleListener::done); + listeners.forEach(SampleListener::done); - if (reporter != null) { - reporter.report(); - } + if (reporter != null) { + reporter.report(); + } - if (reporter != null) { - reporter.stop(); - } + if (reporter != null) { + reporter.stop(); + } + // Temp hack to get some general timings on the OAuth2 Authorize worker broken down by component + if (workers.get(0) instanceof FusionAuthOAuth2AuthorizeWorker) { + System.out.println("\n\n"); + long total = FusionAuthOAuth2AuthorizeWorker.timing.render + FusionAuthOAuth2AuthorizeWorker.timing.post + FusionAuthOAuth2AuthorizeWorker.timing.token; + long iterationCount = (long) workers.size() * loopCount; - // Temp hack to get some general timings on the OAuth2 Authorize worker broken down by component - if (workers.get(0) instanceof FusionAuthOAuth2AuthorizeWorker) { - System.out.println("\n\n"); - long total = FusionAuthOAuth2AuthorizeWorker.timing.render + FusionAuthOAuth2AuthorizeWorker.timing.post + FusionAuthOAuth2AuthorizeWorker.timing.token; - long iterationCount = (long) workers.size() * loopCount; + int renderPercent = (int) (FusionAuthOAuth2AuthorizeWorker.timing.render * 100.0 / total + 0.5); + int postPercent = (int) (FusionAuthOAuth2AuthorizeWorker.timing.post * 100.0 / total + 0.5); + int tokenPercent = (int) (FusionAuthOAuth2AuthorizeWorker.timing.token * 100.0 / total + 0.5); - int renderPercent = (int) (FusionAuthOAuth2AuthorizeWorker.timing.render * 100.0 / total + 0.5); - int postPercent = (int) (FusionAuthOAuth2AuthorizeWorker.timing.post * 100.0 / total + 0.5); - int tokenPercent = (int) (FusionAuthOAuth2AuthorizeWorker.timing.token * 100.0 / total + 0.5); + System.out.println("Render: " + FusionAuthOAuth2AuthorizeWorker.timing.render + " ms, Average: " + FusionAuthOAuth2AuthorizeWorker.timing.render / (iterationCount) + " ms, " + (renderPercent) + "%"); + System.out.println("Post: " + FusionAuthOAuth2AuthorizeWorker.timing.post + " ms, Average: " + FusionAuthOAuth2AuthorizeWorker.timing.post / (iterationCount) + " ms, " + (postPercent) + "%"); + System.out.println("Token: " + FusionAuthOAuth2AuthorizeWorker.timing.token + " ms, Average: " + FusionAuthOAuth2AuthorizeWorker.timing.token / (iterationCount) + " ms, " + (tokenPercent) + "%"); + System.out.println("\n\n"); + } - System.out.println("Render: " + FusionAuthOAuth2AuthorizeWorker.timing.render + " ms, Average: " + FusionAuthOAuth2AuthorizeWorker.timing.render / (iterationCount) + " ms, " + (renderPercent) + "%"); - System.out.println("Post: " + FusionAuthOAuth2AuthorizeWorker.timing.post + " ms, Average: " + FusionAuthOAuth2AuthorizeWorker.timing.post / (iterationCount) + " ms, " + (postPercent) + "%"); - System.out.println("Token: " + FusionAuthOAuth2AuthorizeWorker.timing.token + " ms, Average: " + FusionAuthOAuth2AuthorizeWorker.timing.token / (iterationCount) + " ms, " + (tokenPercent) + "%"); - System.out.println("\n\n"); + done = true; + initialized = true; + return this; } - - done = true; - initialized = true; - return this; } public void initialize() { diff --git a/src/main/resources/Create-Applications.json b/src/main/resources/Create-Applications.json index 051140e..0d751cc 100644 --- a/src/main/resources/Create-Applications.json +++ b/src/main/resources/Create-Applications.json @@ -1,6 +1,6 @@ { "loopCount": 100, - "workerCount": 20, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.FusionAuthWorkerFactory", "attributes": { diff --git a/src/main/resources/Create-Tenants.json b/src/main/resources/Create-Tenants.json index b4cf59c..433475a 100644 --- a/src/main/resources/Create-Tenants.json +++ b/src/main/resources/Create-Tenants.json @@ -1,6 +1,6 @@ { "loopCount": 100, - "workerCount": 20, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.FusionAuthWorkerFactory", "attributes": { diff --git a/src/main/resources/HTTP.json b/src/main/resources/HTTP.json index 4ca8b5b..bebf747 100644 --- a/src/main/resources/HTTP.json +++ b/src/main/resources/HTTP.json @@ -1,6 +1,6 @@ { "loopCount": 500000, - "workerCount": 20, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.HTTPWorkerFactory", "attributes": { diff --git a/src/main/resources/OAuth2-AuthorizationCodeGrant.json b/src/main/resources/OAuth2-AuthorizationCodeGrant.json index 97f9913..3dff2f2 100644 --- a/src/main/resources/OAuth2-AuthorizationCodeGrant.json +++ b/src/main/resources/OAuth2-AuthorizationCodeGrant.json @@ -1,6 +1,6 @@ { "loopCount": 100, - "workerCount": 20, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.FusionAuthWorkerFactory", "attributes": { diff --git a/src/main/resources/User-Logins.json b/src/main/resources/User-Logins.json index 895b7c7..2a7d00a 100644 --- a/src/main/resources/User-Logins.json +++ b/src/main/resources/User-Logins.json @@ -1,6 +1,6 @@ { "loopCount": 1000, - "workerCount": 50, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.FusionAuthWorkerFactory", "attributes": { diff --git a/src/main/resources/User-Registrations.json b/src/main/resources/User-Registrations.json index d6ac77d..7eea697 100644 --- a/src/main/resources/User-Registrations.json +++ b/src/main/resources/User-Registrations.json @@ -1,6 +1,6 @@ { "loopCount": 1000, - "workerCount": 10, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.FusionAuthWorkerFactory", "attributes": { diff --git a/src/main/resources/User-RetrieveEmail.json b/src/main/resources/User-RetrieveEmail.json index 6b152e3..58e0836 100644 --- a/src/main/resources/User-RetrieveEmail.json +++ b/src/main/resources/User-RetrieveEmail.json @@ -1,6 +1,6 @@ { "loopCount": 2000, - "workerCount": 25, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.FusionAuthWorkerFactory", "attributes": { diff --git a/src/main/resources/User-Search.json b/src/main/resources/User-Search.json index cbf618d..e18e635 100644 --- a/src/main/resources/User-Search.json +++ b/src/main/resources/User-Search.json @@ -1,6 +1,6 @@ { "loopCount": 2000, - "workerCount": 25, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.FusionAuthWorkerFactory", "attributes": { diff --git a/src/main/resources/User-SearchData.json b/src/main/resources/User-SearchData.json index 46621aa..d0e6a18 100644 --- a/src/main/resources/User-SearchData.json +++ b/src/main/resources/User-SearchData.json @@ -1,6 +1,6 @@ { "loopCount": 2000, - "workerCount": 25, + "workerCount": 100, "workerFactory": { "className": "io.fusionauth.load.FusionAuthWorkerFactory", "attributes": { diff --git a/src/main/script/load-test.sh b/src/main/script/load-test.sh index b835a0a..392741a 100755 --- a/src/main/script/load-test.sh +++ b/src/main/script/load-test.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # -# Copyright (c) 2022, FusionAuth, All Rights Reserved +# Copyright (c) 2022-2025, FusionAuth, All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,4 +33,4 @@ if [[ $# > 1 && $1 == "--suspend" ]]; then shift fi -~/dev/java/current17/bin/java ${suspend} -cp "${CLASSPATH}" io.fusionauth.load.LoadRunner $@ +~/dev/java/current21/bin/java ${suspend} -cp "${CLASSPATH}" io.fusionauth.load.LoadRunner $@ From fce5e59195976462af484320a73e1446b5018be4 Mon Sep 17 00:00:00 2001 From: Daniel DeGroff Date: Tue, 28 Jan 2025 13:43:23 -0700 Subject: [PATCH 2/2] PR feedback --- src/main/java/io/fusionauth/load/Foreman.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/io/fusionauth/load/Foreman.java b/src/main/java/io/fusionauth/load/Foreman.java index 8dbbc95..b18bd67 100644 --- a/src/main/java/io/fusionauth/load/Foreman.java +++ b/src/main/java/io/fusionauth/load/Foreman.java @@ -47,9 +47,7 @@ public class Foreman implements Buildable { public Foreman execute() throws InterruptedException { initialize(); - // Note that we are going to use virtual threads, so in theory we don't need a pool, but we are trying to simulate clients or workers - // so keep the thread pool and just use a virtual factory. - try (ExecutorService pool = Executors.newFixedThreadPool(workers.size())) { + try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) { // Gradually build up workers, to reduce the chance of failures while we get going. for (Worker worker : workers) {