Back to skills
SKILL.md
Performance Testing
ASecurityLoad testing with k6/Artillery, response time thresholds, memory leak detection, N+1 query detection, and CI integration.
- 530 stars
- 0 votes
- 0 copies
- 1 view
- Added May 29, 2026
Works with
Security analysis
96/100- Installs packages at runtime which could introduce malicious dependencies
npx -y skills add vibeeval/vibecosystem --skill performance-testing --agent claude-codeAre you the author of Performance Testing?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/vibeeval-performance-testing)---
name: performance-testing
description: Load testing with k6/Artillery, response time thresholds, memory leak detection, N+1 query detection, and CI integration.
---
# Performance Testing
## k6 Script Patterns
### Basic scenario with stages
```javascript
// k6 run load-test.js
import http from 'k6/http'
import { check, sleep } from 'k6'
export const options = {
stages: [
{ duration: '30s', target: 10 }, // ramp up
{ duration: '1m', target: 50 }, // hold load
{ duration: '30s', target: 0 }, // ramp down
],
thresholds: {
http_req_duration: ['p(95)<200', 'p(99)<500'],
http_req_failed: ['rate<0.01'], // < 1% error rate
},
}
export default function () {
const res = http.get('https://api.example.com/users')
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 200ms': (r) => r.timings.duration < 200,
})
sleep(1)
}
```
### POST with auth
```javascript
export default function () {
const payload = JSON.stringify({ email: 'test@example.com', password: 'secret' })
const headers = { 'Content-Type': 'application/json' }
const res = http.post(`${BASE_URL}/auth/login`, payload, { headers })
const token = res.json('token')
http.get(`${BASE_URL}/profile`, {
headers: { Authorization: `Bearer ${token}` },
})
}
```
## Load Test Types
| Type | Duration | Target VU | Purpose |
|------|----------|-----------|---------|
| Smoke | 1 min | 1-5 | Verify script works, no regressions |
| Load | 30 min | expected peak | Normal production conditions |
| Stress | 60 min | 2-3x peak | Find breaking point |
| Spike | 2 min | 10x peak → 0 | Sudden traffic burst behavior |
| Soak | 4-8 hours | 80% peak | Memory leaks, degradation over time |
## Threshold Definitions
```javascript
export const options = {
thresholds: {
// Response time
http_req_duration: ['p(95)<200', 'p(99)<500', 'avg<100'],
// Error rate
http_req_failed: ['rate<0.01'], // < 1%
// Custom metric for specific endpoint
'http_req_duration{name:login}': ['p(95)<300'],
// Checks pass rate
checks: ['rate>0.99'],
},
}
```
## CI Integration (GitHub Actions + k6)
```yaml
# .github/workflows/perf.yml
name: Performance Tests
on:
pull_request:
branches: [main]
jobs:
k6:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run k6 smoke test
uses: grafana/k6-action@v0.3.1
with:
filename: tests/perf/smoke.js
flags: --out json=results.json
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: k6-results
path: results.json
```
## Memory Leak Detection (Node.js)
### Heap snapshot approach
```bash
# Start with --inspect
node --inspect --expose-gc server.js
# In Chrome DevTools → Memory → Take heap snapshot
# Run load, take another snapshot
# Compare: growing retained objects = leak
```
### Programmatic detection
```javascript
import v8 from 'v8'
function checkHeap(label) {
const stats = v8.getHeapStatistics()
console.log(`[${label}] Heap used: ${Math.round(stats.used_heap_size / 1024 / 1024)}MB`)
}
setInterval(() => checkHeap('monitor'), 30_000)
```
### Common leak patterns to watch
```javascript
// BAD: event listener never removed
emitter.on('data', handler) // grows on every request
// GOOD: cleanup in teardown
emitter.on('data', handler)
return () => emitter.off('data', handler)
// BAD: unbounded cache
const cache = {}
cache[userId] = data // never evicted
// GOOD: bounded cache
import LRU from 'lru-cache'
const cache = new LRU({ max: 1000, ttl: 1000 * 60 * 5 })
```
## N+1 Query Detection
### pg_stat_statements (PostgreSQL)
```sql
-- Enable extension
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- Find repetitive queries during a load test window
SELECT
query,
calls,
mean_exec_time,
total_exec_time
FROM pg_stat_statements
WHERE calls > 100
ORDER BY calls DESC
LIMIT 20;
```
### Query logging (development)
```javascript
// Prisma: log all queries
const prisma = new PrismaClient({
log: ['query'],
})
// Detect N+1: same query fired N times in a request
// Fix: use include/select or DataLoader
```
### DataLoader pattern (N+1 fix)
```javascript
import DataLoader from 'dataloader'
const userLoader = new DataLoader(async (ids) => {
const users = await db.user.findMany({ where: { id: { in: ids } } })
return ids.map(id => users.find(u => u.id === id))
})
// In resolver — batches automatically
const user = await userLoader.load(post.authorId)
```
## Web Vitals / Lighthouse CI
```yaml
# .github/workflows/lhci.yml
- name: Lighthouse CI
run: |
npm install -g @lhci/cli
lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
```
```json
// lighthouserc.json
{
"ci": {
"assert": {
"assertions": {
"categories:performance": ["error", { "minScore": 0.8 }],
"first-contentful-paint": ["error", { "maxNumericValue": 2000 }],
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }]
}
}
}
}
```
## Trend Tracking
Store k6 results to Grafana/InfluxDB for trend visualization:
```bash
k6 run --out influxdb=http://localhost:8086/k6 load-test.js
```
Or export JSON and compare baselines:
```bash
k6 run --out json=results-$(git rev-parse --short HEAD).json load-test.js
```
Attribution
Comments
Loading comments…