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,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