perf-skills 1.0.3 → 2.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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -1
- package/package.json +2 -2
- package/skills/perf/SKILL.md +228 -72
- package/skills/perf/evals/evals.json +149 -0
- package/skills/perf/references/tools/gatling.md +286 -286
- package/skills/perf/references/tools/jmeter.md +251 -251
- package/skills/perf/references/tools/k6.md +269 -269
- package/skills/perf/references/tools/locust.md +133 -133
- package/skills/perf/references/topics/correlation.md +406 -0
- package/skills/perf/references/topics/database-testing.md +8 -8
- package/skills/perf/references/topics/modern-architectures.md +15 -15
- package/skills/perf/references/topics/observability.md +3 -3
- package/skills/perf/references/topics/production-testing.md +13 -13
- package/skills/perf/references/topics/protocol-testing.md +15 -15
- package/skills/perf/references/topics/results-analysis.md +13 -13
- package/skills/perf/references/topics/script-generation.md +9 -9
- package/skills/perf/references/topics/test-data.md +6 -6
- package/skills/perf/references/topics/test-execution.md +5 -5
- package/skills/perf/references/topics/workload-design.md +3 -3
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
# Protocol-Specific Performance Testing
|
|
2
2
|
|
|
3
|
-
Covers gRPC, GraphQL, WebSocket/SSE, and message queue (Kafka, RabbitMQ, SQS) load testing
|
|
3
|
+
Covers gRPC, GraphQL, WebSocket/SSE, and message queue (Kafka, RabbitMQ, SQS) load testing - protocols with unique challenges beyond standard HTTP/REST.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## gRPC Performance Testing
|
|
8
8
|
|
|
9
|
-
gRPC uses HTTP/2, Protocol Buffers, and persistent connections
|
|
9
|
+
gRPC uses HTTP/2, Protocol Buffers, and persistent connections - fundamentally different from REST at the wire level.
|
|
10
10
|
|
|
11
11
|
### Key Challenges
|
|
12
12
|
|
|
13
13
|
| Challenge | Why It Matters |
|
|
14
14
|
|---|---|
|
|
15
15
|
| **Protobuf compilation** | Tests require compiled `.proto` definitions; can't just send raw JSON |
|
|
16
|
-
| **Connection multiplexing** | HTTP/2 multiplexes streams over a single connection
|
|
16
|
+
| **Connection multiplexing** | HTTP/2 multiplexes streams over a single connection - fewer connections needed but different bottleneck profile |
|
|
17
17
|
| **Streaming** | Unary, server-streaming, client-streaming, and bidirectional-streaming each need different test strategies |
|
|
18
18
|
| **Metadata vs headers** | gRPC metadata is the equivalent of HTTP headers; auth tokens go here |
|
|
19
|
-
| **Error codes** | gRPC uses its own status codes (OK, UNAVAILABLE, DEADLINE_EXCEEDED)
|
|
19
|
+
| **Error codes** | gRPC uses its own status codes (OK, UNAVAILABLE, DEADLINE_EXCEEDED) - not HTTP status codes |
|
|
20
20
|
|
|
21
21
|
### Tool Support
|
|
22
22
|
|
|
@@ -58,14 +58,14 @@ export default function () {
|
|
|
58
58
|
- **Connection reuse**: Keep connections open across iterations; don't connect/close per request.
|
|
59
59
|
- **Streaming throughput**: For server-streaming RPCs, measure messages-per-second, not just request latency.
|
|
60
60
|
- **Deadline propagation**: Set gRPC deadlines in tests to match production timeouts.
|
|
61
|
-
- **Load balancer awareness**: gRPC over HTTP/2 with persistent connections can cause uneven load across backends
|
|
62
|
-
- **Protobuf payload size**: Binary encoding is smaller than JSON
|
|
61
|
+
- **Load balancer awareness**: gRPC over HTTP/2 with persistent connections can cause uneven load across backends - test with client-side load balancing or L7 proxy.
|
|
62
|
+
- **Protobuf payload size**: Binary encoding is smaller than JSON - adjust throughput expectations accordingly.
|
|
63
63
|
|
|
64
64
|
---
|
|
65
65
|
|
|
66
66
|
## GraphQL Performance Testing
|
|
67
67
|
|
|
68
|
-
GraphQL introduces query complexity as a variable
|
|
68
|
+
GraphQL introduces query complexity as a variable - the same endpoint can serve trivially cheap or devastatingly expensive requests.
|
|
69
69
|
|
|
70
70
|
### Key Challenges
|
|
71
71
|
|
|
@@ -79,7 +79,7 @@ GraphQL introduces query complexity as a variable — the same endpoint can serv
|
|
|
79
79
|
|
|
80
80
|
### Testing Strategy
|
|
81
81
|
|
|
82
|
-
1. **Catalog production queries**: Extract real queries from logs or APM traces
|
|
82
|
+
1. **Catalog production queries**: Extract real queries from logs or APM traces - don't invent synthetic queries.
|
|
83
83
|
2. **Categorize by cost**: Light (single field), medium (nested 2 levels), heavy (deep joins, lists).
|
|
84
84
|
3. **Build a realistic query mix**: Weight tests by actual production query distribution.
|
|
85
85
|
4. **Test with and without caching**: GraphQL caching (DataLoader, CDN) dramatically changes performance profiles.
|
|
@@ -136,7 +136,7 @@ Persistent connections have fundamentally different scaling characteristics from
|
|
|
136
136
|
|
|
137
137
|
| Challenge | Why It Matters |
|
|
138
138
|
|---|---|
|
|
139
|
-
| **Connection count** | Each VU holds an open connection
|
|
139
|
+
| **Connection count** | Each VU holds an open connection - tests memory/fd limits, not just CPU |
|
|
140
140
|
| **Message throughput** | Measure messages/sec independently from connection count |
|
|
141
141
|
| **Backpressure** | What happens when the server can't send fast enough? |
|
|
142
142
|
| **Reconnection behavior** | Dropped connections should auto-reconnect; test the reconnect storm |
|
|
@@ -194,7 +194,7 @@ export default function () {
|
|
|
194
194
|
|
|
195
195
|
### WebSocket Testing Strategy
|
|
196
196
|
|
|
197
|
-
1. **Connection ramp**: Gradually open connections
|
|
197
|
+
1. **Connection ramp**: Gradually open connections - don't blast 10k connections at once.
|
|
198
198
|
2. **Separate connection test from message test**: First, find max stable connections; then test message throughput at a stable connection count.
|
|
199
199
|
3. **Measure server-side**: File descriptor count, memory per connection, event loop lag.
|
|
200
200
|
4. **Test reconnection storms**: Kill server, observe client reconnection behavior and server recovery.
|
|
@@ -211,7 +211,7 @@ Testing Kafka, RabbitMQ, SQS, and similar systems requires measuring producer th
|
|
|
211
211
|
|---|---|
|
|
212
212
|
| **Producer throughput** | Messages/sec the system can ingest |
|
|
213
213
|
| **Consumer lag** | How far behind consumers fall under load |
|
|
214
|
-
| **End-to-end latency** | Time from produce to consume
|
|
214
|
+
| **End-to-end latency** | Time from produce to consume - the real user-facing metric |
|
|
215
215
|
| **Partition scaling** | Kafka throughput scales with partitions; test with realistic partition counts |
|
|
216
216
|
| **Message ordering** | Under load, verify ordering guarantees still hold |
|
|
217
217
|
| **Dead letter queues** | Verify failed messages route correctly under pressure |
|
|
@@ -256,10 +256,10 @@ export function teardown() {
|
|
|
256
256
|
### Message Queue Testing Strategy
|
|
257
257
|
|
|
258
258
|
1. **Test producers and consumers separately first**, then together.
|
|
259
|
-
2. **Measure consumer lag over time**
|
|
260
|
-
3. **Test with realistic message sizes**
|
|
261
|
-
4. **Verify idempotency**
|
|
262
|
-
5. **Test partition rebalancing**
|
|
259
|
+
2. **Measure consumer lag over time** - a growing lag under steady load indicates a bottleneck.
|
|
260
|
+
3. **Test with realistic message sizes** - a 100-byte message vs a 1MB payload have very different throughput ceilings.
|
|
261
|
+
4. **Verify idempotency** - under load with retries, duplicate messages should not corrupt state.
|
|
262
|
+
5. **Test partition rebalancing** - add/remove consumers during a test to verify rebalance behavior.
|
|
263
263
|
|
|
264
264
|
---
|
|
265
265
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Results Analysis
|
|
2
2
|
|
|
3
|
-
Interpreting performance test results is a skill in itself. Raw numbers are meaningless without context
|
|
3
|
+
Interpreting performance test results is a skill in itself. Raw numbers are meaningless without context - this reference covers how to read, analyze, and act on performance data.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -9,12 +9,12 @@ Interpreting performance test results is a skill in itself. Raw numbers are mean
|
|
|
9
9
|
| Metric | Description | Target |
|
|
10
10
|
|---|---|---|
|
|
11
11
|
| **Throughput** | Requests per second (RPS / TPS) | ≥ defined baseline |
|
|
12
|
-
| **Response Time (p50)** | Median
|
|
13
|
-
| **Response Time (p95)** | 95% of requests faster than this
|
|
14
|
-
| **Response Time (p99)** | Tail latency
|
|
12
|
+
| **Response Time (p50)** | Median - half of requests faster than this | Per SLA |
|
|
13
|
+
| **Response Time (p95)** | 95% of requests faster than this - main SLA metric | Per SLA |
|
|
14
|
+
| **Response Time (p99)** | Tail latency - reveals worst-case behavior | Per SLA |
|
|
15
15
|
| **Error Rate** | % of failed requests | < 1% (or as defined) |
|
|
16
16
|
| **Active VUs / Concurrency** | Number of simulated users at any point | Matches load profile |
|
|
17
|
-
| **Network I/O** | Bytes/sec in and out
|
|
17
|
+
| **Network I/O** | Bytes/sec in and out - detects bandwidth bottlenecks | Headroom vs NIC capacity |
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -47,9 +47,9 @@ Example: 100 requests
|
|
|
47
47
|
| Column | Meaning |
|
|
48
48
|
|---|---|
|
|
49
49
|
| # Samples | Total requests sent |
|
|
50
|
-
| Average | Mean response time (ms)
|
|
50
|
+
| Average | Mean response time (ms) - use sparingly |
|
|
51
51
|
| Min / Max | Absolute floor and ceiling |
|
|
52
|
-
| Std. Dev. | Variance
|
|
52
|
+
| Std. Dev. | Variance - high deviation = inconsistent behavior |
|
|
53
53
|
| Error % | Percentage of failed requests |
|
|
54
54
|
| Throughput | Requests/second |
|
|
55
55
|
| KB/sec | Network throughput |
|
|
@@ -57,11 +57,11 @@ Example: 100 requests
|
|
|
57
57
|
|
|
58
58
|
### HTML Dashboard
|
|
59
59
|
Generated with `-e -o results/dashboard` flag. Key graphs:
|
|
60
|
-
- **Response Time Over Time**
|
|
61
|
-
- **Transactions Per Second**
|
|
62
|
-
- **Response Time Percentiles**
|
|
63
|
-
- **Active Threads Over Time**
|
|
64
|
-
- **Errors Over Time**
|
|
60
|
+
- **Response Time Over Time** - spot degradation trends
|
|
61
|
+
- **Transactions Per Second** - confirm throughput matches expectation
|
|
62
|
+
- **Response Time Percentiles** - see distribution
|
|
63
|
+
- **Active Threads Over Time** - correlate with response time
|
|
64
|
+
- **Errors Over Time** - when and what errors spike
|
|
65
65
|
|
|
66
66
|
### JTL Analysis (programmatic)
|
|
67
67
|
```python
|
|
@@ -191,7 +191,7 @@ Sudden jump at a load threshold = system has hit a constraint (thread limit, DB
|
|
|
191
191
|
|
|
192
192
|
## Comparing Runs (Trend Analysis)
|
|
193
193
|
|
|
194
|
-
Always compare the current run against a baseline
|
|
194
|
+
Always compare the current run against a baseline - never evaluate a run in isolation.
|
|
195
195
|
|
|
196
196
|
| Metric | Run 1 (Baseline) | Run 2 | Delta | Status |
|
|
197
197
|
|---|---|---|---|---|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Script Generation & Best Practices
|
|
2
2
|
|
|
3
|
-
This reference covers how to write robust, realistic, and maintainable performance test scripts
|
|
3
|
+
This reference covers how to write robust, realistic, and maintainable performance test scripts - regardless of tool. Apply these principles to JMeter, k6, Gatling, Locust, or any other tool.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -55,7 +55,7 @@ Correlation is extracting a dynamic value from a response and using it in a subs
|
|
|
55
55
|
### Correlation debugging approach
|
|
56
56
|
1. Run the script with 1 VU.
|
|
57
57
|
2. Look for HTTP 4xx errors (especially 403 Forbidden, 422 Unprocessable Entity).
|
|
58
|
-
3. Check the request body/headers
|
|
58
|
+
3. Check the request body/headers - is a token missing or stale?
|
|
59
59
|
4. Use the tool's debug output or proxy (Fiddler, Charles) to inspect live traffic.
|
|
60
60
|
5. Identify where the value appears in a *previous* response.
|
|
61
61
|
6. Add an extractor at that response, reference the variable in the failing request.
|
|
@@ -64,14 +64,14 @@ Correlation is extracting a dynamic value from a response and using it in a subs
|
|
|
64
64
|
|
|
65
65
|
## Assertions (Why They're Non-Negotiable)
|
|
66
66
|
|
|
67
|
-
Without assertions, your test might generate 1000 RPS of 404 responses or empty bodies
|
|
67
|
+
Without assertions, your test might generate 1000 RPS of 404 responses or empty bodies - and your metrics will look "fine."
|
|
68
68
|
|
|
69
69
|
### What to assert
|
|
70
|
-
- **Status code**
|
|
71
|
-
- **Response body**
|
|
72
|
-
- **Response time**
|
|
73
|
-
- **Response size**
|
|
74
|
-
- **Content-Type header**
|
|
70
|
+
- **Status code** - the obvious one; but also check for 200s that contain error bodies.
|
|
71
|
+
- **Response body** - key field exists and has expected value.
|
|
72
|
+
- **Response time** - flag individual responses over SLA as failures.
|
|
73
|
+
- **Response size** - detect truncated or empty bodies.
|
|
74
|
+
- **Content-Type header** - ensure you got JSON, not an HTML error page.
|
|
75
75
|
|
|
76
76
|
### Assertion layering
|
|
77
77
|
|
|
@@ -141,7 +141,7 @@ Most tools handle cookies automatically. Verify the Cookie Manager is enabled.
|
|
|
141
141
|
1. Authenticate in `setup()` or `on_start()`.
|
|
142
142
|
2. Store the token in session variables.
|
|
143
143
|
3. Add as Authorization header to subsequent requests.
|
|
144
|
-
4. Handle token expiry
|
|
144
|
+
4. Handle token expiry - check for 401 responses and re-authenticate.
|
|
145
145
|
|
|
146
146
|
```javascript
|
|
147
147
|
// k6: handle token refresh
|
|
@@ -6,10 +6,10 @@ Test data is a first-class concern in performance testing. Bad data causes false
|
|
|
6
6
|
|
|
7
7
|
## Why Test Data Matters
|
|
8
8
|
|
|
9
|
-
- **Authentication tokens** expire
|
|
9
|
+
- **Authentication tokens** expire - hardcoded tokens fail within minutes.
|
|
10
10
|
- **Unique constraints** (email, username, order number) cause failures at concurrency > 1.
|
|
11
11
|
- **State-dependent flows** (checkout, approval) require data in the right state before the test.
|
|
12
|
-
- **Cache effects**
|
|
12
|
+
- **Cache effects** - if 1000 VUs all hit the same product ID, cache hit rates are unrealistically high; spread data across IDs.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ Test data is a first-class concern in performance testing. Bad data causes false
|
|
|
17
17
|
|
|
18
18
|
| Test Type | Data Strategy |
|
|
19
19
|
|---|---|
|
|
20
|
-
| Smoke (1 VU) | Minimal
|
|
20
|
+
| Smoke (1 VU) | Minimal - single known-good user/record |
|
|
21
21
|
| Load (realistic VUs) | Pool of pre-created users/records matching production volume |
|
|
22
22
|
| Stress (pushing limits) | Large pool; no uniqueness conflicts |
|
|
23
23
|
| Soak (hours) | Rotating/recyclable data; handle state cleanup |
|
|
@@ -78,7 +78,7 @@ TO '/tmp/users.csv' CSV HEADER;
|
|
|
78
78
|
Best for: data that must be in a specific state per VU (unique cart, session, transaction).
|
|
79
79
|
|
|
80
80
|
```javascript
|
|
81
|
-
// k6 setup()
|
|
81
|
+
// k6 setup() - run before VUs start
|
|
82
82
|
export function setup() {
|
|
83
83
|
const orders = [];
|
|
84
84
|
for (let i = 0; i < 200; i++) {
|
|
@@ -103,7 +103,7 @@ def on_start(self):
|
|
|
103
103
|
|
|
104
104
|
### Strategy 4: Faker / Synthetic Data Generation
|
|
105
105
|
|
|
106
|
-
Best for: registration flows, profile creation, form submission
|
|
106
|
+
Best for: registration flows, profile creation, form submission - where each VU needs unique PII-like data.
|
|
107
107
|
|
|
108
108
|
```python
|
|
109
109
|
# Python (Locust or data generation script)
|
|
@@ -169,7 +169,7 @@ Each VU/iteration picks the next row. Ensures even distribution.
|
|
|
169
169
|
Better for cache-busting tests where you want realistic cache miss rates.
|
|
170
170
|
|
|
171
171
|
### Unique-Per-VU
|
|
172
|
-
Each VU gets a dedicated row
|
|
172
|
+
Each VU gets a dedicated row - critical for state-dependent flows (user owns specific order). Use VU index (`__VU` in k6, `${__threadNum}` in JMeter) to deterministically select a row.
|
|
173
173
|
|
|
174
174
|
```javascript
|
|
175
175
|
// k6: VU-indexed data
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Test Execution
|
|
2
2
|
|
|
3
|
-
Covers how to run performance tests reliably
|
|
3
|
+
Covers how to run performance tests reliably - locally, in CI/CD pipelines, distributed across injectors, and in the cloud.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ Covers how to run performance tests reliably — locally, in CI/CD pipelines, di
|
|
|
19
19
|
|
|
20
20
|
- Always use **non-GUI / headless mode** for actual load runs (GUI mode adds overhead).
|
|
21
21
|
- Run scripts from the same network segment as the system under test when possible.
|
|
22
|
-
- Monitor injector machine resources during the test
|
|
22
|
+
- Monitor injector machine resources during the test - if CPU/memory of the injector saturates, results are invalid.
|
|
23
23
|
- Set JVM heap appropriately for JMeter: `JVM_ARGS="-Xms2g -Xmx4g" jmeter -n -t test.jmx`
|
|
24
24
|
|
|
25
25
|
---
|
|
@@ -47,7 +47,7 @@ remote_hosts=injector-1:1099,injector-2:1099,injector-3:1099
|
|
|
47
47
|
jmeter -n -t test.jmx -r -l results.jtl -Jthreads=300 -Jduration=600
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
**Ports to open:** 1099 (RMI control), 50000+ (dynamic data ports)
|
|
50
|
+
**Ports to open:** 1099 (RMI control), 50000+ (dynamic data ports) - firewall rules critical.
|
|
51
51
|
|
|
52
52
|
**Scaling:** Each injector can typically handle 300–500 VUs for HTTP at moderate response times. For 10k VUs, plan 20–30 injectors.
|
|
53
53
|
|
|
@@ -136,7 +136,7 @@ Code PR → Build → Unit/Integration Tests → [Performance Gate] →
|
|
|
136
136
|
└─ PASS → Deploy / FAIL → Block
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
### GitHub Actions
|
|
139
|
+
### GitHub Actions - k6
|
|
140
140
|
|
|
141
141
|
```yaml
|
|
142
142
|
name: Performance Gate
|
|
@@ -183,7 +183,7 @@ jobs:
|
|
|
183
183
|
# For additional reporting, parse results.json here
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
-
### GitHub Actions
|
|
186
|
+
### GitHub Actions - JMeter
|
|
187
187
|
|
|
188
188
|
```yaml
|
|
189
189
|
- name: Run JMeter Tests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Workload Design
|
|
2
2
|
|
|
3
|
-
Workload design is the most impactful phase of performance testing. A poorly designed workload produces results that are irrelevant to production behavior
|
|
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
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -153,7 +153,7 @@ Every test must have explicit, measurable SLA targets before execution. Negotiat
|
|
|
153
153
|
| Max response time | < 5,000ms |
|
|
154
154
|
|
|
155
155
|
**Why percentiles matter more than averages:**
|
|
156
|
-
Average response time hides outliers. p95 tells you what 95% of users experience
|
|
156
|
+
Average response time hides outliers. p95 tells you what 95% of users experience - averages can look fine while 10% of users timeout.
|
|
157
157
|
|
|
158
158
|
---
|
|
159
159
|
|
|
@@ -185,7 +185,7 @@ Pacing is used when you want `N` iterations per hour per VU regardless of respon
|
|
|
185
185
|
|
|
186
186
|
## Ramp-Up Strategy
|
|
187
187
|
|
|
188
|
-
Bad ramp-up causes a "thundering herd"
|
|
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
189
|
|
|
190
190
|
**Good ramp-up:**
|
|
191
191
|
- Rule of thumb: Ramp to 100% over at least 5–10 minutes for large tests.
|