perf-skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +8 -0
- package/README.md +227 -0
- package/package.json +42 -0
- package/perf-skills/.claude-plugin/plugin.json +5 -0
- package/perf-skills/SKILL.md +169 -0
- package/perf-skills/references/tools/gatling.md +286 -0
- package/perf-skills/references/tools/jmeter.md +251 -0
- package/perf-skills/references/tools/k6.md +269 -0
- package/perf-skills/references/tools/loadrunner.md +116 -0
- package/perf-skills/references/tools/locust.md +133 -0
- package/perf-skills/references/tools/neoload.md +61 -0
- package/perf-skills/references/tools/octoperf.md +50 -0
- package/perf-skills/references/topics/database-testing.md +214 -0
- package/perf-skills/references/topics/modern-architectures.md +314 -0
- package/perf-skills/references/topics/observability.md +201 -0
- package/perf-skills/references/topics/production-testing.md +214 -0
- package/perf-skills/references/topics/protocol-testing.md +274 -0
- package/perf-skills/references/topics/results-analysis.md +244 -0
- package/perf-skills/references/topics/script-generation.md +221 -0
- package/perf-skills/references/topics/test-data.md +196 -0
- package/perf-skills/references/topics/test-execution.md +281 -0
- package/perf-skills/references/topics/workload-design.md +207 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# Test Execution
|
|
2
|
+
|
|
3
|
+
Covers how to run performance tests reliably — locally, in CI/CD pipelines, distributed across injectors, and in the cloud.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Execution Modes
|
|
8
|
+
|
|
9
|
+
| Mode | When to Use | Tooling |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Local (single process) | Development, debugging, smoke tests | All tools |
|
|
12
|
+
| Distributed (multiple agents) | > 500–1000 VUs, high RPS | JMeter, Locust, k6 |
|
|
13
|
+
| Cloud (managed) | Production-scale, geographic distribution | Grafana Cloud, BlazeMeter, OctoPerf, Gatling Enterprise, NeoLoad Cloud |
|
|
14
|
+
| CI/CD (automated) | Regression gate, scheduled runs | All tools via CLI |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Local Execution Best Practices
|
|
19
|
+
|
|
20
|
+
- Always use **non-GUI / headless mode** for actual load runs (GUI mode adds overhead).
|
|
21
|
+
- Run scripts from the same network segment as the system under test when possible.
|
|
22
|
+
- Monitor injector machine resources during the test — if CPU/memory of the injector saturates, results are invalid.
|
|
23
|
+
- Set JVM heap appropriately for JMeter: `JVM_ARGS="-Xms2g -Xmx4g" jmeter -n -t test.jmx`
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Distributed Execution
|
|
28
|
+
|
|
29
|
+
### JMeter Distributed
|
|
30
|
+
|
|
31
|
+
**Architecture:**
|
|
32
|
+
```
|
|
33
|
+
Controller (orchestrate) → Injector 1 (1/N VUs)
|
|
34
|
+
→ Injector 2 (1/N VUs)
|
|
35
|
+
→ Injector 3 (1/N VUs)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Setup:**
|
|
39
|
+
```bash
|
|
40
|
+
# On each injector
|
|
41
|
+
./bin/jmeter-server -Djava.rmi.server.hostname=<injector-ip>
|
|
42
|
+
|
|
43
|
+
# On controller (bin/jmeter.properties)
|
|
44
|
+
remote_hosts=injector-1:1099,injector-2:1099,injector-3:1099
|
|
45
|
+
|
|
46
|
+
# Run (controller distributes test plan and aggregates results)
|
|
47
|
+
jmeter -n -t test.jmx -r -l results.jtl -Jthreads=300 -Jduration=600
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Ports to open:** 1099 (RMI control), 50000+ (dynamic data ports) — firewall rules critical.
|
|
51
|
+
|
|
52
|
+
**Scaling:** Each injector can typically handle 300–500 VUs for HTTP at moderate response times. For 10k VUs, plan 20–30 injectors.
|
|
53
|
+
|
|
54
|
+
### k6 Distributed
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# k6 Operator on Kubernetes (recommended for large-scale k6)
|
|
58
|
+
# 1. Install k6 Operator
|
|
59
|
+
kubectl apply -f https://github.com/grafana/k6-operator/releases/latest/download/bundle.yaml
|
|
60
|
+
|
|
61
|
+
# 2. Create TestRun resource
|
|
62
|
+
apiVersion: k6.io/v1alpha1
|
|
63
|
+
kind: TestRun
|
|
64
|
+
metadata:
|
|
65
|
+
name: k6-load-test
|
|
66
|
+
spec:
|
|
67
|
+
parallelism: 5 # 5 pods, each running part of the test
|
|
68
|
+
script:
|
|
69
|
+
configMap:
|
|
70
|
+
name: k6-test-script
|
|
71
|
+
file: test.js
|
|
72
|
+
arguments: --out influxdb=http://influx:8086/k6
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Locust Distributed
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Master (web UI on port 8089)
|
|
79
|
+
locust -f locustfile.py --master --expect-workers=4
|
|
80
|
+
|
|
81
|
+
# Workers (connect to master)
|
|
82
|
+
locust -f locustfile.py --worker --master-host=<master-ip>
|
|
83
|
+
|
|
84
|
+
# Headless distributed
|
|
85
|
+
locust -f locustfile.py --master --headless \
|
|
86
|
+
--users 2000 --spawn-rate 50 --run-time 10m \
|
|
87
|
+
--expect-workers=4 --html=report.html
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Cloud Execution
|
|
93
|
+
|
|
94
|
+
### k6 → Grafana Cloud
|
|
95
|
+
```bash
|
|
96
|
+
# Authenticate
|
|
97
|
+
k6 login cloud --token $K6_CLOUD_TOKEN
|
|
98
|
+
|
|
99
|
+
# Run in cloud
|
|
100
|
+
k6 cloud --vus 1000 --duration 10m script.js
|
|
101
|
+
|
|
102
|
+
# Or tag the project
|
|
103
|
+
k6 cloud --project-id $PROJECT_ID script.js
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### JMeter → BlazeMeter / OctoPerf
|
|
107
|
+
```bash
|
|
108
|
+
# BlazeMeter CLI
|
|
109
|
+
bzt tests/load-test.jmx \
|
|
110
|
+
-o modules.blazemeter.token=$BZ_TOKEN \
|
|
111
|
+
-o modules.blazemeter.project="MyApp Load Test" \
|
|
112
|
+
-o modules.blazemeter.concurrency=1000 \
|
|
113
|
+
-o modules.blazemeter.duration=600
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### AWS (DIY Cloud Execution)
|
|
117
|
+
```bash
|
|
118
|
+
# Spin up EC2 injectors via Terraform, then run JMeter/k6
|
|
119
|
+
# Use Auto Scaling Groups for burst capacity
|
|
120
|
+
# Use S3 to store results, CloudWatch for metrics
|
|
121
|
+
|
|
122
|
+
# Cost optimization: use Spot instances for injectors (price vs reliability tradeoff)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## CI/CD Integration Patterns
|
|
128
|
+
|
|
129
|
+
### Performance Gate Pattern
|
|
130
|
+
```
|
|
131
|
+
Code PR → Build → Unit/Integration Tests → [Performance Gate] → Deploy
|
|
132
|
+
│
|
|
133
|
+
├─ Run smoke (1 VU)
|
|
134
|
+
├─ Run load test (target VUs)
|
|
135
|
+
├─ Check SLA thresholds
|
|
136
|
+
└─ PASS → Deploy / FAIL → Block
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### GitHub Actions — k6
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
name: Performance Gate
|
|
143
|
+
|
|
144
|
+
on:
|
|
145
|
+
pull_request:
|
|
146
|
+
branches: [main]
|
|
147
|
+
schedule:
|
|
148
|
+
- cron: '0 2 * * *' # Nightly soak test
|
|
149
|
+
|
|
150
|
+
jobs:
|
|
151
|
+
load-test:
|
|
152
|
+
runs-on: ubuntu-latest
|
|
153
|
+
steps:
|
|
154
|
+
- uses: actions/checkout@v4
|
|
155
|
+
|
|
156
|
+
- name: Start Application (staging)
|
|
157
|
+
run: docker-compose up -d
|
|
158
|
+
|
|
159
|
+
- name: Wait for readiness
|
|
160
|
+
run: |
|
|
161
|
+
timeout 60 sh -c 'until curl -sf http://localhost:8080/health; do sleep 2; done'
|
|
162
|
+
|
|
163
|
+
- name: Run k6 Load Test
|
|
164
|
+
uses: grafana/k6-action@v0.3.1
|
|
165
|
+
with:
|
|
166
|
+
filename: tests/load/main.js
|
|
167
|
+
flags: --out json=results/results.json
|
|
168
|
+
env:
|
|
169
|
+
BASE_URL: http://localhost:8080
|
|
170
|
+
TARGET_VUS: 50
|
|
171
|
+
DURATION: 5m
|
|
172
|
+
|
|
173
|
+
- name: Upload Results
|
|
174
|
+
uses: actions/upload-artifact@v4
|
|
175
|
+
if: always()
|
|
176
|
+
with:
|
|
177
|
+
name: k6-results-${{ github.run_id }}
|
|
178
|
+
path: results/
|
|
179
|
+
|
|
180
|
+
- name: Check SLA Thresholds
|
|
181
|
+
run: |
|
|
182
|
+
# k6 already exits non-zero if thresholds fail
|
|
183
|
+
# For additional reporting, parse results.json here
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### GitHub Actions — JMeter
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
- name: Run JMeter Tests
|
|
190
|
+
run: |
|
|
191
|
+
jmeter -n \
|
|
192
|
+
-t tests/load.jmx \
|
|
193
|
+
-l results/results.jtl \
|
|
194
|
+
-e -o results/dashboard \
|
|
195
|
+
-Jbase_url=${{ vars.STAGING_URL }} \
|
|
196
|
+
-Jthreads=100 \
|
|
197
|
+
-Jduration=300
|
|
198
|
+
|
|
199
|
+
- name: Check Error Rate
|
|
200
|
+
run: |
|
|
201
|
+
python3 scripts/check_jtl.py results/results.jtl \
|
|
202
|
+
--max-error-rate 1.0 \
|
|
203
|
+
--max-p95 500
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### GitLab CI
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
performance-test:
|
|
210
|
+
stage: performance
|
|
211
|
+
image: grafana/k6:latest
|
|
212
|
+
script:
|
|
213
|
+
- k6 run --vus $VUS --duration $DURATION tests/load.js
|
|
214
|
+
variables:
|
|
215
|
+
VUS: "100"
|
|
216
|
+
DURATION: "5m"
|
|
217
|
+
BASE_URL: $STAGING_URL
|
|
218
|
+
artifacts:
|
|
219
|
+
reports:
|
|
220
|
+
performance: results.json
|
|
221
|
+
paths:
|
|
222
|
+
- results.json
|
|
223
|
+
only:
|
|
224
|
+
- main
|
|
225
|
+
- /^release\/.*$/
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Jenkins Pipeline
|
|
229
|
+
|
|
230
|
+
```groovy
|
|
231
|
+
stage('Performance Test') {
|
|
232
|
+
steps {
|
|
233
|
+
sh '''
|
|
234
|
+
k6 run \
|
|
235
|
+
--out json=results.json \
|
|
236
|
+
-e BASE_URL=${STAGING_URL} \
|
|
237
|
+
tests/load.js
|
|
238
|
+
'''
|
|
239
|
+
}
|
|
240
|
+
post {
|
|
241
|
+
always {
|
|
242
|
+
archiveArtifacts artifacts: 'results.json'
|
|
243
|
+
perfReport 'results.json' // Jenkins Performance Plugin
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Environment Isolation for Tests
|
|
252
|
+
|
|
253
|
+
### Dedicated Staging Environment
|
|
254
|
+
- Mirror production architecture (same instance types, same DB configuration).
|
|
255
|
+
- No shared services with other teams during test window.
|
|
256
|
+
- Disable rate limiting or coordinate with the team running the test.
|
|
257
|
+
|
|
258
|
+
### Test Window Management
|
|
259
|
+
- Book a test window and notify all stakeholders.
|
|
260
|
+
- Disable non-essential background jobs that would skew results.
|
|
261
|
+
- Pre-warm caches if testing steady-state behavior (not cold-start).
|
|
262
|
+
|
|
263
|
+
### Infrastructure Monitoring During Test
|
|
264
|
+
Always monitor the injector resources alongside the SUT:
|
|
265
|
+
- CPU, memory, network I/O on each injector.
|
|
266
|
+
- If injector CPU hits 80%+, the injector is the bottleneck, not the SUT.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Test Execution Checklist
|
|
271
|
+
|
|
272
|
+
- [ ] Non-GUI / headless mode configured
|
|
273
|
+
- [ ] Injector capacity verified (CPU, network bandwidth, thread limit)
|
|
274
|
+
- [ ] Data files deployed to all injector nodes
|
|
275
|
+
- [ ] SUT environment isolated and confirmed healthy
|
|
276
|
+
- [ ] APM/observability hooks active
|
|
277
|
+
- [ ] SLA thresholds configured for CI pass/fail gate
|
|
278
|
+
- [ ] Results output path configured (JTL, JSON, InfluxDB)
|
|
279
|
+
- [ ] Smoke test (1 VU) passed before full run
|
|
280
|
+
- [ ] Team notified of test window
|
|
281
|
+
- [ ] Rollback plan in place (especially for production testing)
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Workload Design
|
|
2
|
+
|
|
3
|
+
Workload design is the most impactful phase of performance testing. A poorly designed workload produces results that are irrelevant to production behavior — no matter how well-scripted the test is.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Step 1: Understand Production Traffic
|
|
8
|
+
|
|
9
|
+
Before writing a single line of script, gather real data:
|
|
10
|
+
|
|
11
|
+
| Data Source | What to Extract |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Web server access logs | Request URLs, methods, frequency, user session patterns |
|
|
14
|
+
| APM (Datadog, Dynatrace) | Transaction mix, throughput by endpoint, peak hours |
|
|
15
|
+
| CDN logs | Real geographic distribution and peak RPS |
|
|
16
|
+
| Analytics (GA, Mixpanel) | User journeys, funnel drop-offs, session duration |
|
|
17
|
+
| Database slow query logs | Expensive queries to include in test scenarios |
|
|
18
|
+
|
|
19
|
+
**Key questions to answer:**
|
|
20
|
+
- What is the **peak concurrent user count** (not sessions per day)?
|
|
21
|
+
- What is the **throughput target** (RPS or TPS) at peak?
|
|
22
|
+
- What is the **transaction mix** (% login, % browse, % checkout)?
|
|
23
|
+
- What is the **session length** and **average think time**?
|
|
24
|
+
- Are there **batch or background jobs** running during peak?
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Step 2: Define the Workload Model
|
|
29
|
+
|
|
30
|
+
### Concurrency Model Selection
|
|
31
|
+
|
|
32
|
+
**Closed Model (VU-based)**
|
|
33
|
+
- VU count is fixed; next iteration starts only when previous completes.
|
|
34
|
+
- Think time is part of the model.
|
|
35
|
+
- Best for: web apps where users "hold" sessions.
|
|
36
|
+
- Tools: JMeter (Thread Groups), Gatling (`rampUsers`), Locust.
|
|
37
|
+
|
|
38
|
+
**Open Model (Arrival-rate)**
|
|
39
|
+
- New requests arrive at a fixed rate regardless of outstanding requests.
|
|
40
|
+
- More realistic for APIs, microservices, and public-facing systems.
|
|
41
|
+
- Best for: REST APIs, event-driven systems, queueing scenarios.
|
|
42
|
+
- Tools: k6 (`constant-arrival-rate`), Gatling (`constantUsersPerSec`), JMeter (Throughput Shaping Timer).
|
|
43
|
+
|
|
44
|
+
### When to Use Each
|
|
45
|
+
|
|
46
|
+
| Scenario | Model |
|
|
47
|
+
|---|---|
|
|
48
|
+
| E-commerce web app with sessions | Closed |
|
|
49
|
+
| REST API serving mobile clients | Open |
|
|
50
|
+
| Microservice with queue consumer | Open |
|
|
51
|
+
| Banking portal with session timeouts | Closed |
|
|
52
|
+
| Public API with rate limiting | Open |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Step 3: Define the Load Profile
|
|
57
|
+
|
|
58
|
+
### Common Load Profiles
|
|
59
|
+
|
|
60
|
+
**Ramp-Up + Steady State + Ramp-Down**
|
|
61
|
+
```
|
|
62
|
+
VUs │ ▁▂▃▄▅▆▇█████████▇▆▅▄▃▂▁
|
|
63
|
+
│
|
|
64
|
+
└────────────────────────────── Time
|
|
65
|
+
[Ramp 5m][ Hold 10m ][Down 2m]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Step Load (Staircase)**
|
|
69
|
+
```
|
|
70
|
+
VUs │ ████
|
|
71
|
+
│ ████ ████ ████
|
|
72
|
+
│ ████ ████ ████ ████
|
|
73
|
+
└──────────────────────── Time
|
|
74
|
+
```
|
|
75
|
+
Use for: finding the load level at which behavior degrades.
|
|
76
|
+
|
|
77
|
+
**Spike Test**
|
|
78
|
+
```
|
|
79
|
+
VUs │ ▐█▌
|
|
80
|
+
│ ▐█▌
|
|
81
|
+
│ ██████████▐█▌█████████
|
|
82
|
+
└──────────────────────────── Time
|
|
83
|
+
```
|
|
84
|
+
Use for: testing autoscaling response, queue behavior under burst.
|
|
85
|
+
|
|
86
|
+
**Soak/Endurance**
|
|
87
|
+
```
|
|
88
|
+
VUs │ ████████████████████████
|
|
89
|
+
└──────────────────────────── Time (2–8 hours)
|
|
90
|
+
```
|
|
91
|
+
Use for: detecting memory leaks, connection pool exhaustion, log rotation issues.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Step 4: Calculate Concurrency
|
|
96
|
+
|
|
97
|
+
### Little's Law (the fundamental formula)
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
N = λ × W
|
|
101
|
+
|
|
102
|
+
N = average concurrency (VUs)
|
|
103
|
+
λ = arrival rate (requests/sec = throughput)
|
|
104
|
+
W = average response time + think time (seconds)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Example:**
|
|
108
|
+
- Target: 1,000 RPS
|
|
109
|
+
- Average response time: 200ms = 0.2s
|
|
110
|
+
- Average think time: 3s
|
|
111
|
+
- W = 0.2 + 3 = 3.2s
|
|
112
|
+
- N = 1,000 × 3.2 = **3,200 VUs needed**
|
|
113
|
+
|
|
114
|
+
This is why VU count alone is meaningless without knowing think time and response time targets.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Step 5: Define Transaction Mix
|
|
119
|
+
|
|
120
|
+
Break down the workload into realistic business transactions:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
Example: E-commerce application
|
|
124
|
+
|
|
125
|
+
Transaction | % of Traffic | Think Time
|
|
126
|
+
---------------------|--------------|------------
|
|
127
|
+
Homepage browse | 40% | 5–15s
|
|
128
|
+
Product search | 25% | 3–8s
|
|
129
|
+
Product detail view | 20% | 5–10s
|
|
130
|
+
Add to cart | 10% | 2–5s
|
|
131
|
+
Checkout + payment | 5% | 30–60s
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Implement in scripts:
|
|
135
|
+
- **JMeter**: Use Throughput Controller (% mode) or separate Thread Groups with ratios.
|
|
136
|
+
- **k6**: Use scenarios with `weight` or multiple VU groups with proportional counts.
|
|
137
|
+
- **Gatling**: Use `Population` with `userWeight`.
|
|
138
|
+
- **Locust**: Use `@task(weight)` decorator.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Step 6: Set SLA Targets
|
|
143
|
+
|
|
144
|
+
Every test must have explicit, measurable SLA targets before execution. Negotiate these with stakeholders:
|
|
145
|
+
|
|
146
|
+
| Metric | Threshold Example |
|
|
147
|
+
|---|---|
|
|
148
|
+
| p50 (median) response time | < 200ms |
|
|
149
|
+
| p95 response time | < 500ms |
|
|
150
|
+
| p99 response time | < 1,500ms |
|
|
151
|
+
| Error rate | < 1% |
|
|
152
|
+
| Throughput floor | ≥ 500 RPS |
|
|
153
|
+
| Max response time | < 5,000ms |
|
|
154
|
+
|
|
155
|
+
**Why percentiles matter more than averages:**
|
|
156
|
+
Average response time hides outliers. p95 tells you what 95% of users experience — averages can look fine while 10% of users timeout.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Think Time Design
|
|
161
|
+
|
|
162
|
+
Think time simulates the time a real user spends reading a page, filling a form, or deciding what to do next.
|
|
163
|
+
|
|
164
|
+
| Distribution | When to use |
|
|
165
|
+
|---|---|
|
|
166
|
+
| Constant (e.g., 3s) | Simple tests, known fixed pacing |
|
|
167
|
+
| Uniform random (1–5s) | Basic variability, most common |
|
|
168
|
+
| Gaussian (mean=3s, std=1s) | Most realistic for web users |
|
|
169
|
+
| Negative exponential | Rare; models Poisson arrival patterns |
|
|
170
|
+
|
|
171
|
+
**Rule of thumb:** Never set think time to 0 in closed-model tests unless explicitly modeling a batch job or API benchmark with no user interaction.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Pacing vs Think Time
|
|
176
|
+
|
|
177
|
+
- **Think time**: Time a VU sleeps *within* a transaction (between pages).
|
|
178
|
+
- **Pacing**: Time between the *start* of each iteration (controls throughput more directly).
|
|
179
|
+
|
|
180
|
+
Pacing is used when you want `N` iterations per hour per VU regardless of response time. Example:
|
|
181
|
+
- Target: 360 transactions/hour per VU = 1 transaction every 10 seconds
|
|
182
|
+
- Pacing = 10s (start next iteration 10s after previous started)
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Ramp-Up Strategy
|
|
187
|
+
|
|
188
|
+
Bad ramp-up causes a "thundering herd" — thousands of VUs hitting the server simultaneously, warming up connection pools, JVM, and caches all at once. This is unrealistic and produces misleading results.
|
|
189
|
+
|
|
190
|
+
**Good ramp-up:**
|
|
191
|
+
- Rule of thumb: Ramp to 100% over at least 5–10 minutes for large tests.
|
|
192
|
+
- Allow cache warm-up (exclude ramp period from SLA analysis).
|
|
193
|
+
- For step tests, hold each step for at least 2–3× the transaction response time.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Checklist Before Writing Scripts
|
|
198
|
+
|
|
199
|
+
- [ ] Production traffic baselines collected (RPS, response time, error rate)
|
|
200
|
+
- [ ] Concurrency calculated (Little's Law applied)
|
|
201
|
+
- [ ] Transaction mix defined (% per transaction type)
|
|
202
|
+
- [ ] Think time distribution specified
|
|
203
|
+
- [ ] Load profile drawn (ramp, hold, spike, soak)
|
|
204
|
+
- [ ] SLA thresholds defined and signed off by stakeholders
|
|
205
|
+
- [ ] Environment capacity verified (enough headroom to push load)
|
|
206
|
+
- [ ] Test data strategy defined (see test-data.md)
|
|
207
|
+
- [ ] Monitoring and APM hooks confirmed (see observability.md)
|