diff --git a/clientlibs/java/CLAUDE.md b/clientlibs/java/CLAUDE.md index feea73a24..883fbc7f4 100644 --- a/clientlibs/java/CLAUDE.md +++ b/clientlibs/java/CLAUDE.md @@ -73,7 +73,18 @@ TestNG is the test framework but there is no `src/test/java` directory — `Quic To run a manual integration test: ```bash mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest" # localhost:3030 +mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest" -Dexec.args="local:3031" # another local port mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest" -Dexec.args="qa" # or staging +mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest" -Dexec.args="https://..." # any base URL +``` + +`QuickTestBurst` takes the same values via `--env` (`--env local:3031`), or a base URL via `--url`. + +`QuickTestBurstUi` serves a local form for `QuickTestBurst` at http://localhost:8090: it starts each run as a separate `QuickTestBurst` process, streams the output to the page, and shows the equivalent command line. Its page is `src/main/resources/org/upgradeplatform/client/quicktest-burst-ui.html`. From the repo root, `yarn quickburst:ui` (`scripts/quickburst-ui.sh`) compiles and starts it: + +```bash +yarn quickburst:ui # http://localhost:8090 +yarn quickburst:ui --port 8091 # another port ``` Only localhost is in the code. `qa` / `staging` (here and in `QuickTestBurst --env`) read the base URL from `UPGRADE_QA_URL` / `UPGRADE_STAGING_URL`, which you export yourself (e.g. in your shell profile) — hosted environments' URLs stay out of this public repo. See `QuickTestHosts`. diff --git a/clientlibs/java/src/main/java/org/upgradeplatform/client/QuickTest.java b/clientlibs/java/src/main/java/org/upgradeplatform/client/QuickTest.java index 582cbbf3e..572b5cb3a 100644 --- a/clientlibs/java/src/main/java/org/upgradeplatform/client/QuickTest.java +++ b/clientlibs/java/src/main/java/org/upgradeplatform/client/QuickTest.java @@ -20,12 +20,24 @@ * *
  * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest"
+ * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest" -Dexec.args="local:3031"
  * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest" -Dexec.args="qa"
  * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest" -Dexec.args="staging"
+ * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTest" -Dexec.args="https://some-host.example.com"
  * 
* - * The optional argument is local (default, http://localhost:3030), qa or staging; qa and staging read - * UPGRADE_QA_URL / UPGRADE_STAGING_URL from the environment (see QuickTestHosts). + * The optional argument is local (default, http://localhost:3030), local:<port> for localhost on another port, + * qa, staging, or a base URL starting with http:// or https://. + *

+ * qa and staging are not built in. Their URLs come from environment variables you set on your own machine, + * which you need to add to your shell profile (e.g. {@code ~/.zshrc}) once: + * + *

+ * export UPGRADE_QA_URL=https://<qa backend host>
+ * export UPGRADE_STAGING_URL=https://<staging backend host>
+ * 
+ * + * See QuickTestHosts for details. Passing a base URL directly needs no variables. */ public class QuickTest { private static final String userId = "quicktest_user_" + System.currentTimeMillis(); @@ -56,9 +68,13 @@ public class QuickTest { logRequest.add(logEntry); } - /** Optional first argument: local (default), qa or staging; see QuickTestHosts for the env vars qa/staging need. */ + /** + * Optional first argument: local (default), local:<port>, qa, staging, or a base URL; see QuickTestHosts for + * the env vars qa/staging need. + */ public static void main(String[] args) throws InterruptedException, ExecutionException { - String hostUrl = QuickTestHosts.forEnv(args.length > 0 ? args[0] : "local"); + String host = args.length > 0 ? args[0] : "local"; + String hostUrl = host.startsWith("http://") || host.startsWith("https://") ? host : QuickTestHosts.forEnv(host); System.out.println("host: " + hostUrl); System.out.println("client_session_id: " + sessionId); ExperimentClient client = new ExperimentClient(userId, context, "BearerToken", sessionId, hostUrl, diff --git a/clientlibs/java/src/main/java/org/upgradeplatform/client/QuickTestBurst.java b/clientlibs/java/src/main/java/org/upgradeplatform/client/QuickTestBurst.java index 0de526122..ccaf20007 100644 --- a/clientlibs/java/src/main/java/org/upgradeplatform/client/QuickTestBurst.java +++ b/clientlibs/java/src/main/java/org/upgradeplatform/client/QuickTestBurst.java @@ -2,13 +2,22 @@ import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jdt.annotation.NonNull; @@ -37,6 +46,12 @@ * aliases) is not counted in any timing. Bursts never overlap. The sizes and waits come from {@code --seed} * (printed on every run), so a run can be repeated exactly, e.g. to compare two backend builds. * + * With {@code --ratePerSecond}, bursts overlap instead: a new user (setup, then its burst) starts every 1/rate + * seconds on average, whether or not earlier users have finished, the way users arrive in production. {@code --loops} + * is then the number of users started (e.g. 360 at 6/s is about a minute). Gaps between starts are random + * (exponential, i.e. Poisson arrivals) and come from {@code --seed}, or exactly 1/rate with {@code --evenArrivals}. + * The summary adds the arrival rate actually achieved and the peak users and pairs in flight at once. + * * Pairs are streamed: a new pair starts only when one of the {@code --concurrency} slots frees up, and nothing is * kept per pair once it finishes except its latency samples, so memory stays flat however many loops run. * @@ -46,20 +61,57 @@ * -Dexec.args="185 --concurrency 50 --env qa --context assign-prog" * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTestBurst" \ * -Dexec.args="--loops 50 --minBurst 10 --maxBurst 180 --minBurstWait 100 --maxBurstWait 1000 --seed 42" + * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTestBurst" \ + * -Dexec.args="--loops 360 --minBurst 1 --maxBurst 50 --ratePerSecond 6 --seed 42 --env qa" + * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTestBurst" \ + * -Dexec.args="--loops 360 --burstTiers 90:1,8:2-10,1:11-30,1:31-200 --ratePerSecond 6 --seed 42 --env qa" + * mvn exec:java -Dexec.mainClass="org.upgradeplatform.client.QuickTestBurst" \ + * -Dexec.args="185 --url https://some-host.example.com" + * + * + * {@code --env qa} and {@code --env staging} are not built in. Their URLs come from environment variables you + * set on your own machine, which you need to add to your shell profile (e.g. {@code ~/.zshrc}) once: + * + *
+ * export UPGRADE_QA_URL=https://<qa backend host>
+ * export UPGRADE_STAGING_URL=https://<staging backend host>
  * 
* - * Options (give either {@code } or both {@code --minBurst} and {@code --maxBurst}): + * See QuickTestHosts for details. {@code --url} takes a base URL directly and needs no variables. + * + * QuickTestBurstUi is a local web page for the same options, if you'd rather fill in a form than write the command. + * + * Options (give {@code }, both {@code --minBurst} and {@code --maxBurst}, or {@code --burstTiers}): *