Skip to content
Back to skills

Perf Loadtest

ASecurity

Run the L2 k6 load harness against the live Aspire-hosted backend and produce a summary + optional dotnet-counters CSV. Use to answer "how does the running system behave under load" — throughput, p95 latency, GC pressure — as opposed to L1 (BenchmarkDotNet, code-level) or L3 (dotnet-trace, attribution).

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 20, 2026
developmentgoshellbashfrontendbackendperformancedocumentation

Works with

  • cli

Security analysis

A100/100

Pro scans all 13 files and shows the line behind each finding

Scanned September 20, 2026

npx -y skills add VirtoCommerce/vc-mcp-testing-module --skill perf-loadtest --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Perf Loadtest?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Perf Loadtest
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/virtocommerce-perf-loadtest/badge)](https://www.skillsdirectory.com/skills/virtocommerce-perf-loadtest)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: perf-loadtest
description: Run the L2 k6 load harness against the live Aspire-hosted backend and produce a summary + optional dotnet-counters CSV. Use to answer "how does the running system behave under load" — throughput, p95 latency, GC pressure — as opposed to L1 (BenchmarkDotNet, code-level) or L3 (dotnet-trace, attribution).
---

# perf-loadtest — L2 of the three-layer performance loop

The middle layer of the loop (**L1 bench → L2 load → L3 diagnose**, see skill `perf-loop`
for the router and cross-layer discipline). L2 drives the real backend with k6 and answers "how
does the system behave under load" — throughput, p95 latency, error rate, GC counters — as
opposed to L1's mocked-I/O code-level verdict or L3's WHY-attribution.

## Running it

> **Resolving `$pluginRoot`**: every command below uses `$pluginRoot` as a placeholder for this
> plugin's active install path — resolve it once per task via `claude plugin list --json` (see
> [`../../knowledge/plugin-root.md`](../../knowledge/plugin-root.md)) and reuse it. It is NOT a
> shell variable and `$CLAUDE_PLUGIN_ROOT` does **not** expand in the Bash tool shell.

```bash
$pluginRoot/skills/perf-loadtest/loadtests/run.sh smoke    # 1 VU × 3 iterations — liveness
$pluginRoot/skills/perf-loadtest/loadtests/run.sh steady   # ramp + hold
```

Target the backend at `perf.backendUrl` from `project-profile.json` (pass it as `BASE_URL`, the
harness's own env knob — k6 has no direct file-read access to the profile). Pass
`perf.platformProcess` as `PLATFORM_PROCESS` so the L3 EventPipe sidecar attaches to the right
backend process (projects that rename the host binary must set this, or the sidecar silently
skips):

```bash
BASE_URL="$(jq -r .perf.backendUrl "${PROJECT_PROFILE_PATH:-project-profile.json}")" \
  STORE_ID="$(jq -r .perf.storeId "${PROJECT_PROFILE_PATH:-project-profile.json}")" \
  CURRENCY_CODE="$(jq -r '.perf.currencyCode // "USD"' "${PROJECT_PROFILE_PATH:-project-profile.json}")" \
  CULTURE_NAME="$(jq -r '.perf.cultureName // "en-US"' "${PROJECT_PROFILE_PATH:-project-profile.json}")" \
  PLATFORM_PROCESS="$(jq -r '.perf.platformProcess // "VirtoCommerce.Platform.Web"' "${PROJECT_PROFILE_PATH:-project-profile.json}")" \
  $pluginRoot/skills/perf-loadtest/loadtests/run.sh steady
```

Full knob list, prerequisites (k6 binary, credentials, `dotnet-counters`), the user-pool mechanism
for multi-user concurrency, and orchestration gotchas: `loadtests/README.md`.

### `perf.loadtest.*` profile keys are operator-bridged, not auto-read

`run.sh` and the k6 scenarios read **env vars only** (k6 has no direct file-read access to the
profile) — the `perf.loadtest` keys in `project-profile.json` are not piped in automatically:

- `perf.loadtest.fixtures.productIds` — export `PRODUCT_IDS=<comma-separated ids>` yourself (e.g.
  `PRODUCT_IDS="$(jq -r '.perf.loadtest.fixtures.productIds | join(",")' "${PROJECT_PROFILE_PATH:-project-profile.json}")"`)
  before invoking `run.sh`; the scenarios read `__ENV.PRODUCT_IDS`, not the profile file.
- `perf.loadtest.seedEndpoint` — currently **reserved/unused**: no script or scenario in this
  harness reads it. Treat it as documentation of where a seed-users endpoint lives, not a wired
  knob, until a consumer is added.

## Consumer scenarios/queries: point `PAYLOAD_DIR` at your own dir — do NOT edit the install

Everything under `loadtests/scenarios/` and `loadtests/queries/` scripts the **standard
vc-frontend** GraphQL operations (`getFullCart`, `addItemsCart`, `createOrderFromCart`) — with one
deliberate exception, `clearCart.graphql`, a schema-valid, vendor-neutral fixture document (no
standard storefront mutation clears a whole cart). If your client project overrides the storefront
schema, the shipped documents may not match it exactly — re-resolve them against your own frontend
before relying on the numbers.

**Do NOT edit the plugin's own `scenarios/`/`queries/`.** This skill runs from an install-managed
clone (marketplace/cache), so edits there are ephemeral (wiped on plugin update) and pollute a repo
you don't own. Instead keep your project's scenarios (+ queries) in a **consumer-owned directory**
and point the harness at it with `PAYLOAD_DIR`:

```bash
# in your repo, e.g.  perf-payload/{scenarios/<name>.js, queries/*.graphql}
PAYLOAD_DIR="$PWD/perf-payload" SCENARIO=<name> \
  BASE_URL=... STORE_ID=... \
  $pluginRoot/skills/perf-loadtest/loadtests/run.sh steady
```

`run.sh` assembles a temp run-dir combining the plugin's generic transport (`lib/auth.js`,
`lib/gql.js`) + `config.js` with your `PAYLOAD_DIR/scenarios/` (and `queries/` if present), so you
reuse the stable auth/gql layer and own only the project-specific documents. `PAYLOAD_DIR` must
contain a `scenarios/` dir; `queries/` is optional. Keep the transport generic — never copy `lib/`
into your payload. Set `STORE_ID` (backs `config.js`'s `STORE.storeId`) from `perf.storeId`.

## Role in the loop

Use L2 to answer "how does N/RATE affect latency/GC on the real stack?" — for "did my code
change regress?" use L1 (`perf-benchmark`); for "who is responsible?" run L3
(`perf-trace` skill / `perftools/`) under an L2 scenario driving load.

Files in this skill

  • SKILL.md5.1 KB
  • loadtests/README.md11.8 KB
  • loadtests/config.js458 B
  • loadtests/lib/auth.js2.3 KB
  • loadtests/lib/gql.js3.1 KB
  • loadtests/queries/addItemsCart.graphql1.1 KB
  • loadtests/queries/clearCart.graphql409 B
  • loadtests/queries/createOrderFromCart.graphql3.8 KB
  • loadtests/queries/getFullCart.graphql4.7 KB
  • loadtests/run.sh10.2 KB
  • loadtests/scenarios/cart-order-loop.js10 KB
  • loadtests/scenarios/cart-read-loop.js9.3 KB
  • loadtests/tools/parse_rep.mjs3.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…