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,214 @@
|
|
|
1
|
+
# Database Performance Testing
|
|
2
|
+
|
|
3
|
+
Covers load testing databases directly (JDBC, connection pools, query concurrency) — not just testing the application layer that sits in front of them.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Test the Database Directly
|
|
8
|
+
|
|
9
|
+
| Scenario | Why Direct DB Testing |
|
|
10
|
+
|---|---|
|
|
11
|
+
| **New schema or index changes** | Validate query performance under concurrency before deploying |
|
|
12
|
+
| **Connection pool tuning** | Find optimal pool size, timeout, and eviction settings |
|
|
13
|
+
| **Read replica lag** | Measure replication delay under write-heavy load |
|
|
14
|
+
| **Stored procedure performance** | Procedures with complex logic need load testing independent of the app |
|
|
15
|
+
| **Migration validation** | After DB engine upgrade (e.g., MySQL 5.7 → 8.0), verify no regression |
|
|
16
|
+
| **Deadlock detection** | Concurrent writes to overlapping rows expose locking issues |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Tool Support
|
|
21
|
+
|
|
22
|
+
| Tool | DB Support | How |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| **JMeter** | JDBC Sampler + JDBC Connection Configuration | GUI-based; supports any JDBC-compatible database |
|
|
25
|
+
| **k6** | `xk6-sql` extension | Custom build required; supports Postgres, MySQL, SQLite |
|
|
26
|
+
| **Gatling** | JDBC feeder (read-only) | Feeders can read from DB; no native write support |
|
|
27
|
+
| **pgbench** | PostgreSQL only | Built-in PostgreSQL benchmarking tool |
|
|
28
|
+
| **sysbench** | MySQL, PostgreSQL | Industry-standard DB benchmark tool |
|
|
29
|
+
| **HammerDB** | Oracle, SQL Server, MySQL, PostgreSQL | Open-source TPC-C / TPC-H workload generator |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## JMeter JDBC Testing
|
|
34
|
+
|
|
35
|
+
### Setup
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
1. Add JDBC Connection Configuration (Config Element)
|
|
39
|
+
Variable Name: myDB
|
|
40
|
+
Database URL: jdbc:postgresql://db-host:5432/myapp
|
|
41
|
+
JDBC Driver: org.postgresql.Driver
|
|
42
|
+
Username: perf_test_user
|
|
43
|
+
Password: ${__P(db.password)}
|
|
44
|
+
Max Connections: 50
|
|
45
|
+
Connection Timeout: 10000
|
|
46
|
+
Idle Timeout: 60000
|
|
47
|
+
|
|
48
|
+
2. Add JDBC Request (Sampler)
|
|
49
|
+
Variable Name: myDB
|
|
50
|
+
Query Type: Select Statement
|
|
51
|
+
Query: SELECT * FROM orders WHERE customer_id = ? AND status = ?
|
|
52
|
+
Parameter Values: ${customer_id},${status}
|
|
53
|
+
Parameter Types: INTEGER,VARCHAR
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Common JDBC Test Patterns
|
|
57
|
+
|
|
58
|
+
**Read-heavy workload:**
|
|
59
|
+
```sql
|
|
60
|
+
-- Simulate product catalog browsing
|
|
61
|
+
SELECT p.*, c.name AS category
|
|
62
|
+
FROM products p
|
|
63
|
+
JOIN categories c ON p.category_id = c.id
|
|
64
|
+
WHERE p.active = true
|
|
65
|
+
ORDER BY p.created_at DESC
|
|
66
|
+
LIMIT 20 OFFSET ${__Random(0,1000)};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Write-heavy workload:**
|
|
70
|
+
```sql
|
|
71
|
+
-- Simulate order creation under concurrency
|
|
72
|
+
INSERT INTO orders (customer_id, total, status, created_at)
|
|
73
|
+
VALUES (${customer_id}, ${total}, 'pending', NOW())
|
|
74
|
+
RETURNING id;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Mixed read-write (realistic):**
|
|
78
|
+
Use JMeter Throughput Controller to mix 80% reads / 20% writes.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Connection Pool Testing
|
|
83
|
+
|
|
84
|
+
Connection pool misconfiguration is one of the most common database performance bottlenecks.
|
|
85
|
+
|
|
86
|
+
### What to Test
|
|
87
|
+
|
|
88
|
+
| Parameter | Test Approach |
|
|
89
|
+
|---|---|
|
|
90
|
+
| **Max pool size** | Ramp VUs beyond pool size; measure wait time and connection timeout errors |
|
|
91
|
+
| **Min idle connections** | Start test after idle period; measure cold-start latency vs pre-warmed |
|
|
92
|
+
| **Connection timeout** | Set aggressive timeout; verify graceful degradation when pool exhausted |
|
|
93
|
+
| **Idle timeout / eviction** | Run soak test; verify idle connections get recycled without errors |
|
|
94
|
+
| **Leak detection** | Run soak test; monitor active connection count — it should stabilize, not grow |
|
|
95
|
+
|
|
96
|
+
### Diagnosing Pool Exhaustion
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
Symptoms:
|
|
100
|
+
- Response times spike suddenly at a specific VU count
|
|
101
|
+
- Errors: "Connection pool exhausted", "Timeout waiting for idle object"
|
|
102
|
+
- DB shows fewer active connections than expected
|
|
103
|
+
|
|
104
|
+
Root causes:
|
|
105
|
+
1. Pool max size too small for concurrency
|
|
106
|
+
2. Long-running queries hold connections
|
|
107
|
+
3. Connection leak — app code doesn't close connections in finally/catch blocks
|
|
108
|
+
4. N+1 queries — each request opens multiple connections sequentially
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Key Metrics to Monitor
|
|
112
|
+
|
|
113
|
+
| Metric | Source | Warning Threshold |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| Active connections | HikariCP metrics, PgBouncer stats | > 80% of max pool size |
|
|
116
|
+
| Pending connection requests | Connection pool metrics | > 0 sustained |
|
|
117
|
+
| Connection wait time | Connection pool metrics | > 100ms |
|
|
118
|
+
| Connection creation rate | Pool metrics | High rate = connections not being reused |
|
|
119
|
+
| DB `max_connections` usage | `pg_stat_activity`, MySQL `SHOW STATUS` | > 80% of server limit |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Query Performance Under Concurrency
|
|
124
|
+
|
|
125
|
+
Queries that perform well at 1 VU often degrade at 100 VUs due to lock contention, buffer pool pressure, and I/O saturation.
|
|
126
|
+
|
|
127
|
+
### Testing Approach
|
|
128
|
+
|
|
129
|
+
1. **Baseline single-query latency** — run the query once, capture execution plan.
|
|
130
|
+
2. **Ramp concurrent query execution** — 1, 5, 10, 25, 50, 100 concurrent threads.
|
|
131
|
+
3. **Monitor per-step**: query latency (p50/p95/p99), lock waits, buffer cache hit ratio, disk I/O.
|
|
132
|
+
4. **Identify the knee point** — the concurrency level where latency starts climbing non-linearly.
|
|
133
|
+
|
|
134
|
+
### Slow Query Detection During Load Tests
|
|
135
|
+
|
|
136
|
+
**PostgreSQL:**
|
|
137
|
+
```sql
|
|
138
|
+
-- Enable slow query logging
|
|
139
|
+
ALTER SYSTEM SET log_min_duration_statement = 100; -- Log queries > 100ms
|
|
140
|
+
SELECT pg_reload_conf();
|
|
141
|
+
|
|
142
|
+
-- Check during test
|
|
143
|
+
SELECT query, calls, mean_exec_time, stddev_exec_time
|
|
144
|
+
FROM pg_stat_statements
|
|
145
|
+
ORDER BY mean_exec_time DESC
|
|
146
|
+
LIMIT 20;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**MySQL:**
|
|
150
|
+
```sql
|
|
151
|
+
-- Enable slow query log
|
|
152
|
+
SET GLOBAL slow_query_log = 'ON';
|
|
153
|
+
SET GLOBAL long_query_time = 0.1; -- 100ms
|
|
154
|
+
|
|
155
|
+
-- Check during test
|
|
156
|
+
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 20;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Read Replica Lag Testing
|
|
162
|
+
|
|
163
|
+
For read-replica architectures, verify that replication lag doesn't cause stale reads under write-heavy load.
|
|
164
|
+
|
|
165
|
+
### Test Pattern
|
|
166
|
+
|
|
167
|
+
1. **Write a record** with a unique marker (timestamp + test ID) to the primary.
|
|
168
|
+
2. **Immediately read** the same record from the replica.
|
|
169
|
+
3. **Measure the delay** until the record appears on the replica.
|
|
170
|
+
4. **Track lag over time** — it should stabilize, not grow.
|
|
171
|
+
|
|
172
|
+
### What Replication Lag Breaks
|
|
173
|
+
|
|
174
|
+
- User creates an order → immediately views "My Orders" → order is missing (stale read).
|
|
175
|
+
- Cache invalidation depends on DB triggers → replica lag delays invalidation.
|
|
176
|
+
- Consistency checks fail under load if reading from replica.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Deadlock and Lock Contention Testing
|
|
181
|
+
|
|
182
|
+
### Test Pattern
|
|
183
|
+
|
|
184
|
+
1. Create a scenario where multiple VUs update overlapping rows (e.g., same account balance).
|
|
185
|
+
2. Ramp concurrency and monitor for deadlock errors.
|
|
186
|
+
3. Verify the application handles deadlocks gracefully (retry logic, not crash).
|
|
187
|
+
|
|
188
|
+
### Monitoring Lock Contention
|
|
189
|
+
|
|
190
|
+
**PostgreSQL:**
|
|
191
|
+
```sql
|
|
192
|
+
SELECT blocked.pid, blocked.query AS blocked_query,
|
|
193
|
+
blocking.pid AS blocking_pid, blocking.query AS blocking_query
|
|
194
|
+
FROM pg_stat_activity blocked
|
|
195
|
+
JOIN pg_locks bl ON bl.pid = blocked.pid
|
|
196
|
+
JOIN pg_locks blk ON blk.locktype = bl.locktype
|
|
197
|
+
AND blk.relation = bl.relation AND blk.pid != bl.pid
|
|
198
|
+
JOIN pg_stat_activity blocking ON blocking.pid = blk.pid
|
|
199
|
+
WHERE NOT bl.granted;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Database Testing Checklist
|
|
205
|
+
|
|
206
|
+
- [ ] Dedicated test user/schema created (not production credentials)
|
|
207
|
+
- [ ] Connection pool configuration documented and parameterized
|
|
208
|
+
- [ ] Slow query logging enabled before test start
|
|
209
|
+
- [ ] Read vs write ratio matches production workload
|
|
210
|
+
- [ ] Test data volume matches production scale (row counts, index sizes)
|
|
211
|
+
- [ ] Lock contention and deadlock monitoring active
|
|
212
|
+
- [ ] Replication lag monitoring active (if using read replicas)
|
|
213
|
+
- [ ] Connection pool metrics exposed and dashboarded
|
|
214
|
+
- [ ] Cleanup script ready for test data removal post-test
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Microservices, Kubernetes & Serverless Performance Testing
|
|
2
|
+
|
|
3
|
+
Covers performance testing concerns specific to modern distributed architectures — microservices, container orchestration, service meshes, and serverless platforms.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Microservices Performance Testing
|
|
8
|
+
|
|
9
|
+
Microservices introduce distributed latency, cascading failures, and complex dependency chains that monoliths don't have.
|
|
10
|
+
|
|
11
|
+
### Key Challenges
|
|
12
|
+
|
|
13
|
+
| Challenge | Why It Matters |
|
|
14
|
+
|---|---|
|
|
15
|
+
| **Cascading latency** | A slow downstream service adds latency to every upstream caller |
|
|
16
|
+
| **Retry storms** | Aggressive retry policies under load can amplify failures exponentially |
|
|
17
|
+
| **Circuit breaker validation** | Circuit breakers must open at the right threshold — too early = false positives, too late = cascading failure |
|
|
18
|
+
| **Service mesh overhead** | Sidecar proxies (Envoy/Istio) add 1–5ms per hop — significant in deep call chains |
|
|
19
|
+
| **Fan-out amplification** | One API call that fans out to 10 services means 1 RPS at the gateway = 10 RPS backend |
|
|
20
|
+
| **Data consistency** | Eventual consistency under load can cause stale reads, lost updates, or duplicate processing |
|
|
21
|
+
|
|
22
|
+
### Testing Strategy
|
|
23
|
+
|
|
24
|
+
#### 1. Component-Level Testing (Isolated)
|
|
25
|
+
Test each service independently with mocked dependencies to find per-service bottlenecks:
|
|
26
|
+
- Max RPS before degradation
|
|
27
|
+
- Memory and CPU profile under load
|
|
28
|
+
- Connection pool behavior
|
|
29
|
+
- Error handling for dependency failures (inject 503s, timeouts)
|
|
30
|
+
|
|
31
|
+
#### 2. Integration-Level Testing (Chain)
|
|
32
|
+
Test the full request path through the service chain:
|
|
33
|
+
- End-to-end latency budget (how much latency does each service contribute?)
|
|
34
|
+
- Use distributed tracing (Jaeger, Tempo) to identify the slowest hop
|
|
35
|
+
- Test with realistic inter-service latency (not localhost-to-localhost)
|
|
36
|
+
|
|
37
|
+
#### 3. Resilience Testing (Failure Injection)
|
|
38
|
+
Combine load testing with chaos engineering:
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
// k6 + chaos: run load while injecting failures
|
|
42
|
+
// Step 1: Start load test at 50% target capacity
|
|
43
|
+
// Step 2: Kill a downstream service pod
|
|
44
|
+
// Step 3: Verify circuit breaker opens within SLA (e.g., < 5s)
|
|
45
|
+
// Step 4: Verify error rate stays below threshold
|
|
46
|
+
// Step 5: Restore pod, verify recovery
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Circuit Breaker Testing
|
|
50
|
+
|
|
51
|
+
| State | Test Approach |
|
|
52
|
+
|---|---|
|
|
53
|
+
| **Closed (normal)** | Verify requests pass through with normal latency |
|
|
54
|
+
| **Open (tripped)** | Inject failures until breaker opens; verify fallback response |
|
|
55
|
+
| **Half-Open (recovery)** | After cooldown, verify breaker allows probe requests and recovers |
|
|
56
|
+
|
|
57
|
+
Key metrics: time-to-open, fallback response correctness, recovery time.
|
|
58
|
+
|
|
59
|
+
### Retry Storm Prevention
|
|
60
|
+
|
|
61
|
+
Test that retry policies don't amplify failure:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
Scenario: Service B returns 503
|
|
65
|
+
Without backoff: 100 VUs × 3 retries = 300 RPS hitting an already-failing service
|
|
66
|
+
With exponential backoff + jitter: load dissipates over time
|
|
67
|
+
|
|
68
|
+
Test approach:
|
|
69
|
+
1. Run at target load
|
|
70
|
+
2. Inject 503s from a dependency
|
|
71
|
+
3. Monitor total RPS to that dependency — it should NOT multiply
|
|
72
|
+
4. Verify backoff and jitter are working
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Kubernetes Performance Testing
|
|
78
|
+
|
|
79
|
+
### Pod Autoscaling (HPA) Validation
|
|
80
|
+
|
|
81
|
+
The Horizontal Pod Autoscaler should scale pods in response to load — test that it works correctly.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Monitor HPA during test
|
|
85
|
+
kubectl get hpa -w
|
|
86
|
+
|
|
87
|
+
# Expected behavior during load test:
|
|
88
|
+
# 1. Load increases → CPU/memory rises above target
|
|
89
|
+
# 2. HPA triggers scale-up (observe REPLICAS column increasing)
|
|
90
|
+
# 3. New pods start and become ready
|
|
91
|
+
# 4. Load distributes across new pods
|
|
92
|
+
# 5. Latency stabilizes at acceptable levels
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### What to Test
|
|
96
|
+
|
|
97
|
+
| Concern | Test Approach |
|
|
98
|
+
|---|---|
|
|
99
|
+
| **Scale-up speed** | Time from load increase to pods ready and serving traffic |
|
|
100
|
+
| **Scale-down behavior** | After load drops, verify pods scale down without disrupting active requests |
|
|
101
|
+
| **Pod startup latency** | Time from pod scheduled to first successful health check — critical for burst traffic |
|
|
102
|
+
| **Resource limits impact** | Test with and without CPU/memory limits to find optimal settings |
|
|
103
|
+
| **Pod disruption budget** | During rolling deploys under load, verify PDB prevents downtime |
|
|
104
|
+
| **Node scaling (Cluster Autoscaler)** | If pods can't schedule, verify new nodes provision in time |
|
|
105
|
+
|
|
106
|
+
### Resource Limits and Throttling
|
|
107
|
+
|
|
108
|
+
CPU limits cause CFS throttling — the kernel pauses the container when it exceeds its CPU quota, causing latency spikes.
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
# Common mistake: setting CPU limit too close to request
|
|
112
|
+
resources:
|
|
113
|
+
requests:
|
|
114
|
+
cpu: "500m"
|
|
115
|
+
memory: "512Mi"
|
|
116
|
+
limits:
|
|
117
|
+
cpu: "500m" # Too tight — causes throttling under burst
|
|
118
|
+
memory: "1Gi"
|
|
119
|
+
|
|
120
|
+
# Better: allow burst headroom or remove CPU limit
|
|
121
|
+
resources:
|
|
122
|
+
requests:
|
|
123
|
+
cpu: "500m"
|
|
124
|
+
memory: "512Mi"
|
|
125
|
+
limits:
|
|
126
|
+
# cpu: omitted — allows burst without throttling
|
|
127
|
+
memory: "1Gi"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Test approach**: Run load test, monitor `container_cpu_cfs_throttled_seconds_total` in Prometheus. If throttling is high, response times will have periodic spikes.
|
|
131
|
+
|
|
132
|
+
### Service Mesh Overhead (Istio/Envoy)
|
|
133
|
+
|
|
134
|
+
Sidecar proxies add latency per hop. In a 5-service call chain, that's 10 proxy hops (ingress + egress per service).
|
|
135
|
+
|
|
136
|
+
**Test approach**:
|
|
137
|
+
1. Run load test WITHOUT service mesh → record baseline latency.
|
|
138
|
+
2. Enable service mesh → run same load test.
|
|
139
|
+
3. Compare: the delta is your mesh overhead.
|
|
140
|
+
4. If overhead is too high: tune Envoy concurrency, connection pool settings, or evaluate ambient mesh (Istio ambient mode — no sidecars).
|
|
141
|
+
|
|
142
|
+
### Ingress Controller Performance
|
|
143
|
+
|
|
144
|
+
The ingress controller is often the first bottleneck:
|
|
145
|
+
|
|
146
|
+
| Ingress | Typical Ceiling | Tuning |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| NGINX Ingress | ~10k RPS per pod | `worker-processes`, `keepalive-connections`, multiple replicas |
|
|
149
|
+
| Envoy / Istio Gateway | ~15k RPS per pod | `concurrency`, circuit breaker settings |
|
|
150
|
+
| AWS ALB | Scales automatically | Pre-warm for large tests (request from AWS support) |
|
|
151
|
+
| Traefik | ~8k RPS per pod | `maxIdleConnsPerHost`, worker count |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Serverless Performance Testing
|
|
156
|
+
|
|
157
|
+
Serverless (AWS Lambda, Azure Functions, GCP Cloud Functions) introduces unique latency characteristics that don't exist in traditional deployments.
|
|
158
|
+
|
|
159
|
+
### Key Challenges
|
|
160
|
+
|
|
161
|
+
| Challenge | Why It Matters |
|
|
162
|
+
|---|---|
|
|
163
|
+
| **Cold starts** | First invocation after idle can take 100ms–10s depending on runtime and package size |
|
|
164
|
+
| **Concurrency limits** | Account/region-level limits cap how many functions run in parallel |
|
|
165
|
+
| **Provisioned concurrency** | Pre-warmed instances eliminate cold starts but cost money — need to test the right amount |
|
|
166
|
+
| **Timeout behavior** | Functions have max execution time (Lambda: 15 min) — long-running requests timeout silently |
|
|
167
|
+
| **Memory = CPU** | Lambda allocates CPU proportional to memory — 128MB gets less CPU than 1024MB |
|
|
168
|
+
|
|
169
|
+
### Cold Start Testing
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
// k6: measure cold start latency
|
|
173
|
+
// Strategy: invoke function after a known idle period
|
|
174
|
+
|
|
175
|
+
export const options = {
|
|
176
|
+
scenarios: {
|
|
177
|
+
cold_start: {
|
|
178
|
+
executor: 'per-vu-iterations',
|
|
179
|
+
vus: 1,
|
|
180
|
+
iterations: 1, // Single invocation after idle = cold start
|
|
181
|
+
},
|
|
182
|
+
warm: {
|
|
183
|
+
executor: 'constant-vus',
|
|
184
|
+
vus: 10,
|
|
185
|
+
duration: '5m',
|
|
186
|
+
startTime: '30s', // Start after cold start test
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
thresholds: {
|
|
190
|
+
'http_req_duration{scenario:cold_start}': ['p(95)<3000'], // Cold start SLA
|
|
191
|
+
'http_req_duration{scenario:warm}': ['p(95)<200'], // Warm SLA
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Concurrency Limit Testing
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
Test approach:
|
|
200
|
+
1. Set Lambda reserved concurrency to a known limit (e.g., 100)
|
|
201
|
+
2. Ramp k6 VUs to exceed the limit
|
|
202
|
+
3. Monitor: invocations should plateau at 100 concurrent
|
|
203
|
+
4. Excess requests should get 429 (throttled) responses
|
|
204
|
+
5. Verify client-side retry with backoff handles throttling gracefully
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Provisioned Concurrency Validation
|
|
208
|
+
|
|
209
|
+
1. Configure provisioned concurrency (e.g., 50 instances).
|
|
210
|
+
2. Run a burst test: 50 VUs simultaneously.
|
|
211
|
+
3. Verify: **zero cold starts** — all requests should hit pre-warmed instances.
|
|
212
|
+
4. Run 51+ VUs: the 51st should hit a cold start — verify the cold start latency.
|
|
213
|
+
|
|
214
|
+
### Memory/CPU Tuning Test
|
|
215
|
+
|
|
216
|
+
Lambda CPU is proportional to memory. Run the same workload at different memory settings:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
128MB → p95: 850ms, cost: $0.0001
|
|
220
|
+
256MB → p95: 420ms, cost: $0.00015
|
|
221
|
+
512MB → p95: 210ms, cost: $0.0002
|
|
222
|
+
1024MB → p95: 195ms, cost: $0.0004 ← diminishing returns after this
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Find the memory setting where cost-per-request at target latency is minimized.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Frontend / Browser Performance
|
|
230
|
+
|
|
231
|
+
For user-facing applications, backend load testing alone is insufficient. Browser-level metrics capture what users actually experience.
|
|
232
|
+
|
|
233
|
+
### Core Web Vitals
|
|
234
|
+
|
|
235
|
+
| Metric | What It Measures | Target |
|
|
236
|
+
|---|---|---|
|
|
237
|
+
| **LCP** (Largest Contentful Paint) | Loading performance | < 2.5s |
|
|
238
|
+
| **INP** (Interaction to Next Paint) | Interactivity responsiveness | < 200ms |
|
|
239
|
+
| **CLS** (Cumulative Layout Shift) | Visual stability | < 0.1 |
|
|
240
|
+
|
|
241
|
+
### Tool Support
|
|
242
|
+
|
|
243
|
+
| Tool | Approach |
|
|
244
|
+
|---|---|
|
|
245
|
+
| **k6/browser** | Chromium-based browser testing; can measure web vitals under load |
|
|
246
|
+
| **Lighthouse CI** | Automated Lighthouse audits in CI pipeline |
|
|
247
|
+
| **WebPageTest** | Detailed waterfall analysis from real browsers |
|
|
248
|
+
| **Grafana Synthetic Monitoring** | Scheduled browser tests from global probes |
|
|
249
|
+
|
|
250
|
+
### k6 Browser Example
|
|
251
|
+
|
|
252
|
+
```javascript
|
|
253
|
+
import { browser } from 'k6/browser';
|
|
254
|
+
import { check } from 'k6';
|
|
255
|
+
|
|
256
|
+
export const options = {
|
|
257
|
+
scenarios: {
|
|
258
|
+
browser: {
|
|
259
|
+
executor: 'constant-vus',
|
|
260
|
+
vus: 5,
|
|
261
|
+
duration: '2m',
|
|
262
|
+
options: { browser: { type: 'chromium' } },
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
thresholds: {
|
|
266
|
+
browser_web_vital_lcp: ['p(95)<2500'],
|
|
267
|
+
browser_web_vital_cls: ['p(95)<0.1'],
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export default async function () {
|
|
272
|
+
const page = await browser.newPage();
|
|
273
|
+
try {
|
|
274
|
+
await page.goto(__ENV.BASE_URL);
|
|
275
|
+
await page.waitForSelector('h1');
|
|
276
|
+
check(page, {
|
|
277
|
+
'page loaded': (p) => p.locator('h1').textContent() !== '',
|
|
278
|
+
});
|
|
279
|
+
} finally {
|
|
280
|
+
await page.close();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Performance Budget Integration
|
|
286
|
+
|
|
287
|
+
Define performance budgets in CI to prevent regressions:
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"budgets": [
|
|
292
|
+
{ "metric": "lcp", "budget": 2500 },
|
|
293
|
+
{ "metric": "total-transfer-size", "budget": 500000 },
|
|
294
|
+
{ "metric": "script-transfer-size", "budget": 200000 },
|
|
295
|
+
{ "metric": "third-party-transfer-size", "budget": 100000 }
|
|
296
|
+
]
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Modern Architecture Testing Checklist
|
|
303
|
+
|
|
304
|
+
- [ ] Component-level and integration-level tests defined separately
|
|
305
|
+
- [ ] Circuit breaker thresholds tested (open/close/half-open states)
|
|
306
|
+
- [ ] Retry policies validated (no retry storms under failure)
|
|
307
|
+
- [ ] Service mesh overhead measured and baselined
|
|
308
|
+
- [ ] HPA scale-up and scale-down behavior validated under load
|
|
309
|
+
- [ ] Pod resource limits tested for CFS throttling impact
|
|
310
|
+
- [ ] Ingress controller capacity tested as a potential bottleneck
|
|
311
|
+
- [ ] Serverless cold start latency measured and budgeted
|
|
312
|
+
- [ ] Concurrency limits and provisioned concurrency validated
|
|
313
|
+
- [ ] Frontend Core Web Vitals measured under backend load
|
|
314
|
+
- [ ] Performance budgets defined and enforced in CI
|