Performance TestingIntermediate
Performance Testing Fundamentals
Master the basics of performance testing including load testing, stress testing, and performance optimization
6 min read
...
performanceload-testingoptimizationmonitoring
Introduction
Performance testing ensures your application can handle expected user loads while maintaining acceptable response times. This guide covers fundamental concepts and practical approaches.
Types of Performance Testing
Load Testing
Tests system behavior under expected load conditions.
// Using k6 for load testing
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 100, // 100 virtual users
duration: '5m', // 5 minutes
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests < 500ms
http_req_failed: ['rate<0.01'], // Error rate < 1%
},
};
export default function () {
const res = http.get('https://api.example.com/users');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}Stress Testing
Tests system limits by gradually increasing load beyond normal capacity.
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp up to 100 users
{ duration: '5m', target: 100 }, // Stay at 100 users
{ duration: '2m', target: 200 }, // Ramp up to 200 users
{ duration: '5m', target: 200 }, // Stay at 200 users
{ duration: '2m', target: 300 }, // Push to 300 users
{ duration: '5m', target: 300 }, // Stay at 300 users
{ duration: '2m', target: 0 }, // Ramp down
],
};Spike Testing
Tests system response to sudden traffic spikes.
export const options = {
stages: [
{ duration: '10s', target: 100 }, // Normal load
{ duration: '1m', target: 5000 }, // Sudden spike
{ duration: '3m', target: 5000 }, // Maintain spike
{ duration: '10s', target: 100 }, // Return to normal
],
};Key Performance Metrics
Response Time Metrics
// Monitor various response time percentiles
export const options = {
thresholds: {
'http_req_duration': [
'p(50)<200', // 50th percentile < 200ms
'p(95)<500', // 95th percentile < 500ms
'p(99)<1000', // 99th percentile < 1s
],
},
};Throughput
Requests per second (RPS) your system can handle:
import { Rate } from 'k6/metrics';
const successRate = new Rate('successful_requests');
export default function() {
const res = http.get('https://api.example.com/products');
successRate.add(res.status === 200);
}Error Rate
Percentage of failed requests:
export const options = {
thresholds: {
'http_req_failed': ['rate<0.01'], // Less than 1% errors
},
};Database Performance
Identify Slow Queries
-- PostgreSQL: Find slow queries
SELECT
query,
calls,
total_time,
mean_time,
max_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;Add Indexes
-- Before: Slow query
SELECT * FROM users WHERE email = 'user@example.com';
-- Execution time: 150ms
-- Add index
CREATE INDEX idx_users_email ON users(email);
-- After: Fast query
SELECT * FROM users WHERE email = 'user@example.com';
-- Execution time: 2msUse Connection Pooling
// With connection pooling
import { Pool } from 'pg';
const pool = new Pool({
max: 20, // Maximum connections
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
async function getUser(id) {
const client = await pool.connect();
try {
const result = await client.query('SELECT * FROM users WHERE id = $1', [id]);
return result.rows[0];
} finally {
client.release();
}
}API Performance Optimization
Implement Caching
import Redis from 'ioredis';
const redis = new Redis();
async function getProduct(id) {
// Try cache first
const cached = await redis.get(`product:${id}`);
if (cached) {
return JSON.parse(cached);
}
// Fetch from database
const product = await db.products.findById(id);
// Cache for 1 hour
await redis.setex(`product:${id}`, 3600, JSON.stringify(product));
return product;
}Use Compression
import express from 'express';
import compression from 'compression';
const app = express();
// Enable gzip compression
app.use(compression());
// Response size reduced from 500KB to 50KB
app.get('/api/data', (req, res) => {
res.json(largeDataset);
});Implement Pagination
// Bad: Return all results
app.get('/api/users', async (req, res) => {
const users = await db.users.findAll(); // Could be 100k+ records
res.json(users);
});
// Good: Paginate results
app.get('/api/users', async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 20;
const offset = (page - 1) * limit;
const users = await db.users.findAll({
limit,
offset,
});
const total = await db.users.count();
res.json({
data: users,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit),
},
});
});Frontend Performance
Optimize Bundle Size
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
};Lazy Loading
// React lazy loading
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}Image Optimization
// Next.js Image component (automatic optimization)
import Image from 'next/image';
function ProductImage({ src, alt }) {
return (
<Image
src={src}
alt={alt}
width={800}
height={600}
loading="lazy"
quality={85}
/>
);
}Monitoring and Alerting
Application Performance Monitoring
// Using New Relic
import newrelic from 'newrelic';
app.get('/api/checkout', async (req, res) => {
const transaction = newrelic.getTransaction();
transaction.setAttribute('user_id', req.user.id);
try {
const result = await processCheckout(req.body);
res.json(result);
} catch (error) {
newrelic.noticeError(error);
res.status(500).json({ error: error.message });
}
});Set Up Alerts
# Example: Prometheus alert rules
groups:
- name: api_performance
rules:
- alert: HighResponseTime
expr: http_request_duration_seconds{quantile="0.95"} > 1
for: 5m
labels:
severity: warning
annotations:
summary: "High API response time"
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "High error rate detected"Best Practices
- Set Performance Budgets: Define acceptable limits before building
- Test Early: Performance test during development, not just before release
- Monitor Production: Use APM tools to catch real-world issues
- Optimize Database: Proper indexes and query optimization
- Use CDN: Serve static assets from CDN for faster delivery
- Cache Wisely: Cache at multiple levels (browser, CDN, application, database)
- Regular Testing: Run performance tests as part of CI/CD
Common Performance Bottlenecks
- N+1 Queries: Fetch related data in single query
- Large Payloads: Implement pagination and filtering
- Synchronous Operations: Use async/await and job queues
- Memory Leaks: Profile and fix memory issues
- Unoptimized Images: Compress and properly size images
- Too Many Dependencies: Audit and remove unused packages
Conclusion
Effective performance testing requires:
- Understanding different test types
- Monitoring key metrics
- Optimizing at all layers (DB, API, Frontend)
- Continuous monitoring
- Regular performance testing in CI/CD
Start with realistic goals, measure everything, and optimize based on data.
Tools to Explore
- Load Testing: k6, JMeter, Gatling
- APM: New Relic, Datadog, Dynatrace
- Profiling: Chrome DevTools, Node.js Profiler
- Database: pgAdmin, MySQL Workbench
Comments (0)
Loading comments...