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.
@@ -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)