performance-testing
Load testing, chaos engineering, and performance validation. Prove your system works under pressure with k6, trace correlation, and progressive load profiles.
What this skill does
# Performance Testing
## Core Principle
Unit tests verify correctness. Integration tests verify the stack works. **Load tests reveal bottlenecks that only appear under pressure.**
```
Single Request: 135ms ✓
Under 1000 concurrent: 2550ms ✗
The bottleneck was invisible until you added load.
```
## Required Behaviors
### 1. Progressive Load Profiles
Don't jump to stress testing. Use progressive profiles:
#### Smoke Test: Does It Work?
```javascript
// load-tests/smoke.js
export const options = {
vus: 1,
duration: '1m',
thresholds: {
http_req_failed: ['rate<0.01'],
},
};
```
One user, one minute. If this fails, you have a functional bug, not a performance problem.
#### Load Test: Expected Traffic
```javascript
// load-tests/load.js
export const options = {
stages: [
{ duration: '2m', target: 50 }, // Ramp up to 50 users
{ duration: '5m', target: 50 }, // Stay at 50 users
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
},
};
```
This simulates your expected production traffic.
#### Stress Test: Find the Breaking Point
```javascript
// load-tests/stress.js
export const options = {
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '2m', target: 200 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 300 }, // Where does it break?
{ duration: '5m', target: 300 },
{ duration: '2m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<2000'], // Relaxed threshold
},
};
```
Keep pushing until something breaks. Note what failed first.
#### Soak Test: Memory Leaks and Degradation
```javascript
// load-tests/soak.js
export const options = {
stages: [
{ duration: '5m', target: 50 },
{ duration: '4h', target: 50 }, // Hold for 4 hours
{ duration: '5m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
},
};
```
Run for hours at moderate load. Watch for:
- Memory usage creeping up
- Response times gradually increasing
- Connection leaks
- File handle exhaustion
#### Spike Test: Sudden Bursts
```javascript
// load-tests/spike.js
export const options = {
stages: [
{ duration: '10s', target: 10 }, // Warm up
{ duration: '1m', target: 10 }, // Baseline
{ duration: '10s', target: 500 }, // SPIKE!
{ duration: '3m', target: 500 }, // Hold the spike
{ duration: '10s', target: 10 }, // Scale back down
{ duration: '3m', target: 10 }, // Recovery period
{ duration: '5s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<3000'], // Allow slower during spike
http_req_failed: ['rate<0.05'], // Allow up to 5% errors
},
};
```
Spike tests reveal:
- Does your autoscaler react fast enough?
- Does your load balancer drop connections?
- Do database connection pools handle sudden demand?
- Does the system recover after the spike?
### 2. Connect Load Tests to Traces
Pass trace context from k6 to correlate with OpenTelemetry:
```javascript
// load-tests/orders-with-tracing.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { randomUUID } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js';
export default function () {
const traceId = randomUUID().replace(/-/g, '');
const spanId = randomUUID().replace(/-/g, '').slice(0, 16);
const response = http.post(`${BASE_URL}/api/orders`, payload, {
headers: {
'Content-Type': 'application/json',
'x-api-key': API_KEY,
// W3C Trace Context header
'traceparent': `00-${traceId}-${spanId}-01`,
// Custom header for correlation
'x-load-test-id': __ENV.TEST_RUN_ID || 'local',
},
});
check(response, {
'status is 201': (r) => r.status === 201,
});
sleep(1); // Simulate user think time - prevents accidental DDoS
}
```
**Always include `sleep()`** - Without it, a single VU generates hundreds of requests per second, accidentally DDoS-ing your local machine. The sleep simulates realistic user behavior.
Now you can find your load test requests in Jaeger/Honeycomb:
```
service.name = "orders-api"
duration > 1s
attributes.x-load-test-id = "stress-test-2024-01-15"
```
### 3. Analyze Traces Under Load
Common bottlenecks revealed by load + traces:
| Symptom in Traces | Root Cause | Fix |
|-------------------|------------|-----|
| Long waits before DB query starts | Connection pool exhausted | Increase pool size or reduce query time |
| External API calls taking 10x longer | Rate limiting kicked in | Add caching, request batching |
| Same DB query repeated N times | N+1 query pattern | Use eager loading / joins |
| Memory spans getting longer over time | Memory leak / GC pressure | Profile memory, fix leaks |
| Timeouts only under load | Resource contention | Add connection limits, queuing |
### 4. Set SLOs and Thresholds
Don't just measure—set expectations. k6 thresholds fail your test if SLOs aren't met:
```javascript
export const options = {
thresholds: {
// Response time SLOs
http_req_duration: [
'p(50)<200', // Median under 200ms
'p(95)<500', // 95th percentile under 500ms
'p(99)<1000', // 99th percentile under 1s
],
// Availability SLO
http_req_failed: ['rate<0.001'], // 99.9% success rate
// Custom metrics
'order_created': ['count>100'], // At least 100 orders created
// Per-endpoint thresholds
'http_req_duration{endpoint:create_order}': ['p(95)<800'],
'http_req_duration{endpoint:get_order}': ['p(95)<200'],
},
};
```
### 5. Chaos Engineering
Prove your [resilience patterns](/skills/resilience) actually work by injecting failures.
#### Simple Chaos: Latency Injection
```typescript
// src/test-utils/chaos.ts
export function withLatency<T>(
fn: () => Promise<T>,
options: { minMs: number; maxMs: number }
): () => Promise<T> {
return async () => {
const delay = Math.random() * (options.maxMs - options.minMs) + options.minMs;
await new Promise((resolve) => setTimeout(resolve, delay));
return fn();
};
}
export function withFailureRate<T>(
fn: () => Promise<T>,
failureRate: number, // 0.0 to 1.0
error: Error = new Error('Injected failure')
): () => Promise<T> {
return async () => {
if (Math.random() < failureRate) {
throw error;
}
return fn();
};
}
```
Use in integration tests:
```typescript
// src/orders/create-order.chaos.test.ts
import { withLatency } from '../test-utils/chaos';
import { createOrder } from './create-order';
it('completes within SLO when payment provider is slow', async () => {
const slowPaymentProvider = {
charge: withLatency(
() => Promise.resolve({ transactionId: 'tx-123' }),
{ minMs: 1500, maxMs: 2000 } // 1.5-2s latency
),
};
const start = Date.now();
const result = await createOrder(
{ customerId: 'cust-1', items: [...] },
{ db: mockDb, paymentProvider: slowPaymentProvider }
);
const duration = Date.now() - start;
expect(result.ok).toBe(true);
expect(duration).toBeLessThan(5000); // Still under 5s SLO
});
```
#### Network-Level Chaos with Toxiproxy
For more realistic chaos, use [Toxiproxy](https://github.com/Shopify/toxiproxy):
```yaml
# docker-compose.chaos.yml
services:
toxiproxy:
image: ghcr.io/shopify/toxiproxy
ports:
- "8474:8474" # API
- "5433:5433" # Proxied postgres
postgres:
image: postgres:16
# Toxiproxy sits between app and postgres
```
```typescript
// Configure toxic before load test
import Toxiproxy from 'toxiproxy-node-client';
const toxiproxy = new Toxiproxy('http://localhost:8474');
// Add 500ms latency to database
await toxiproxy.createToxic('postgres', {
name: 'latency',
type: 'latency',
attributes: { latency: 500, jitter: 100 },
});
// Run load test
// Then check: Did connection pool handle the latency?
// Did timeouts fire correctly?
// Did tRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.