Skip to main content

Load Testing Guide

This guide explains how to perform load testing on your Enterprise BOS deployment to validate performance and identify bottlenecks.

Prerequisites​

  • Node.js 18+
  • k6 or Artillery
  • Running instance of the application
  • Test database (separate from production)

Quick Start with k6​

Installation​

# macOS
brew install k6

# Windows
choco install k6

# Linux
sudo apt-get install k6

Basic Load Test​

Create tests/load/basic.js:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
stages: [
{ duration: '30s', target: 20 }, // Ramp up to 20 users
{ duration: '1m', target: 20 }, // Stay at 20 users
{ duration: '30s', target: 50 }, // Ramp up to 50 users
{ duration: '1m', target: 50 }, // Stay at 50 users
{ duration: '30s', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
http_req_failed: ['rate<0.01'], // Less than 1% failure rate
},
};

const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';

export default function () {
// Test public endpoints
const landing = http.get(`${BASE_URL}/`);
check(landing, {
'landing page status is 200': (r) => r.status === 200,
});

// Test API health
const health = http.get(`${BASE_URL}/api/health`);
check(health, {
'health check is 200': (r) => r.status === 200,
});

sleep(1);
}

Run Load Test​

k6 run --env BASE_URL=http://localhost:3000 tests/load/basic.js

Authenticated Load Test​

Create tests/load/authenticated.js:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
stages: [
{ duration: '1m', target: 10 },
{ duration: '2m', target: 10 },
{ duration: '30s', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<1000'],
http_req_failed: ['rate<0.05'],
},
};

const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';

export function setup() {
// Login and get token
const loginRes = http.post(`${BASE_URL}/api/auth/login`, JSON.stringify({
email: 'demo@example.com',
password: 'demo123456',
}), {
headers: { 'Content-Type': 'application/json' },
});

const token = loginRes.json('token');
return { token };
}

export default function (data) {
const headers = {
'Authorization': `Bearer ${data.token}`,
'Content-Type': 'application/json',
};

// Test dashboard data
const dashboard = http.get(`${BASE_URL}/api/dashboard/stats`, { headers });
check(dashboard, {
'dashboard loads': (r) => r.status === 200,
'dashboard response time OK': (r) => r.timings.duration < 500,
});

// Test projects list
const projects = http.get(`${BASE_URL}/api/projects`, { headers });
check(projects, {
'projects load': (r) => r.status === 200,
});

sleep(2);
}

Expected Performance Benchmarks​

Endpointp50p95p99Target RPS
Landing Page<100ms<300ms<500ms100
API Health<50ms<100ms<200ms500
Dashboard Stats<200ms<500ms<1000ms50
Project List<150ms<400ms<800ms100
AI Chat (streaming)<2000ms<5000ms<10000ms10

Database Performance​

Query Performance Tips​

  1. Use indexes - Schema includes indexes on frequently queried columns
  2. Limit result sets - Always paginate large queries
  3. Cache expensive queries - Use dbCache helper for dashboard stats

Connection Pooling​

For Neon/Supabase, connection pooling is handled by the provider. For self-hosted PostgreSQL:

// Recommended pool settings for 50 concurrent users
{
max: 20, // Maximum connections
idleTimeoutMs: 30000,
connectionTimeoutMs: 2000,
}

Monitoring During Tests​

Vercel Dashboard​

  • Real-time function invocations
  • Cold start frequency
  • Error rates

Database Metrics​

  • Active connections
  • Query duration
  • Cache hit ratio

Application Logs​

# Watch structured logs during test
npm run dev 2>&1 | jq '.'

Scaling Recommendations​

UsersInfrastructure
1-100Vercel Hobby + Neon Free
100-1000Vercel Pro + Neon Pro
1000-10000Vercel Pro + Neon Scale + Redis
10000+Dedicated infrastructure

CI/CD Integration​

Add to .github/workflows/load-test.yml:

name: Load Test
on:
schedule:
- cron: '0 2 * * 1' # Weekly Monday 2 AM
workflow_dispatch:

jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: grafana/k6-action@v0.3.1
with:
filename: tests/load/basic.js
env:
BASE_URL: ${{ secrets.STAGING_URL }}

Troubleshooting​

High Response Times​

  1. Check database query performance
  2. Review N+1 queries
  3. Enable caching for repeated queries
  4. Check cold start frequency

High Error Rates​

  1. Review rate limiting configuration
  2. Check database connection limits
  3. Validate authentication token expiry
  4. Review error logs in Sentry