import http from 'k6/http'; import { check, group, sleep } from 'k6'; import { Rate, Trend } from 'k6/metrics'; import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; // Custom metrics const errorRate = new Rate('errors'); const responseTime = new Trend('response_time'); const recoveryTime = new Trend('recovery_time'); const breakingPoint = new Rate('breaking_point_reached'); // Stress test configuration - gradually increase load until system breaks export const options = { scenarios: { // Gradual stress test - find breaking point gradual_stress: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '2m', target: 100 }, { duration: '2m', target: 250 }, { duration: '2m', target: 500 }, { duration: '2m', target: 750 }, { duration: '2m', target: 1000 }, { duration: '2m', target: 1500 }, { duration: '2m', target: 2000 }, { duration: '5m', target: 0 }, // Recovery phase ], tags: { test_type: 'stress_gradual' }, }, // Spike test - sudden high load spike_test: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '1m', target: 100 }, { duration: '30s', target: 2000 }, // Sudden spike { duration: '3m', target: 2000 }, // Sustained high load { duration: '2m', target: 0 }, // Recovery ], tags: { test_type: 'stress_spike' }, }, }, thresholds: { http_req_failed: ['rate<0.05'], // Allow up to 5% errors under stress }, // Stop test if error rate exceeds 50% (breaking point found) teardownTimeout: '5m', }; const BASE_URL = __ENV.BASE_URL || 'http://localhost:8000'; const API_V1 = `${BASE_URL}/api/v1`; // Track system state let systemHealthy = true; let consecutiveErrors = 0; const ERROR_THRESHOLD = 50; // Consider system broken after 50 consecutive errors export function setup() { console.log('Starting stress test - finding breaking point...'); // Baseline health check const startTime = Date.now(); const healthCheck = http.get(`${BASE_URL}/health`); const baselineTime = Date.now() - startTime; console.log(`Baseline health check: ${healthCheck.status}, response time: ${baselineTime}ms`); return { startTime: Date.now(), baselineResponseTime: baselineTime, }; } export default function(data) { const params = { headers: { 'Content-Type': 'application/json', }, }; group('Critical Endpoints Stress', () => { // Health endpoint - primary indicator const healthStart = Date.now(); const healthRes = http.get(`${BASE_URL}/health`, params); const healthDuration = Date.now() - healthStart; const healthCheck = check(healthRes, { 'health responds': (r) => r.status !== 0, 'health response time < 5s': (r) => r.timings.duration < 5000, }); if (!healthCheck) { consecutiveErrors++; errorRate.add(1); } else { consecutiveErrors = 0; errorRate.add(0); } responseTime.add(healthDuration); // Detect breaking point if (consecutiveErrors >= ERROR_THRESHOLD) { breakingPoint.add(1); systemHealthy = false; console.log(`Breaking point detected at ${Date.now() - data.startTime}ms`); } }); group('Database Stress', () => { // Heavy database query - list scenarios with pagination const dbStart = Date.now(); const dbRes = http.get(`${API_V1}/scenarios?page=1&page_size=100`, params); const dbDuration = Date.now() - dbStart; check(dbRes, { 'DB query responds': (r) => r.status !== 0, 'DB query response time < 10s': (r) => r.timings.duration < 10000, }); responseTime.add(dbDuration); }); group('Ingest Stress', () => { // High volume log ingestion const batchSize = randomIntBetween(1, 10); const logs = []; for (let i = 0; i < batchSize; i++) { logs.push({ message: `Stress test log ${randomIntBetween(1, 10000000)}`, source: 'stress-test', level: 'INFO', timestamp: new Date().toISOString(), }); } const ingestStart = Date.now(); const ingestRes = http.batch( logs.map(log => ({ method: 'POST', url: `${BASE_URL}/ingest`, body: JSON.stringify(log), params: { headers: { 'Content-Type': 'application/json', 'X-Scenario-ID': `stress_scenario_${randomIntBetween(1, 10)}`, }, }, })) ); const ingestDuration = Date.now() - ingestStart; const ingestCheck = check(ingestRes, { 'ingest batch processed': (responses) => responses.every(r => r.status === 200 || r.status === 202 || r.status === 429), }); errorRate.add(!ingestCheck); responseTime.add(ingestDuration); }); group('Memory Stress', () => { // Large report generation request const reportStart = Date.now(); const reportRes = http.get(`${API_V1}/reports?page=1&page_size=50`, params); const reportDuration = Date.now() - reportStart; check(reportRes, { 'report query responds': (r) => r.status !== 0, }); responseTime.add(reportDuration); }); // Adaptive sleep based on system health if (systemHealthy) { sleep(randomIntBetween(1, 2)); } else { // During recovery, wait longer between requests sleep(randomIntBetween(3, 5)); // Track recovery const recoveryStart = Date.now(); const recoveryHealth = http.get(`${BASE_URL}/health`, params); recoveryTime.add(Date.now() - recoveryStart); if (recoveryHealth.status === 200) { console.log(`System recovering... Response time: ${recoveryTime.name}`); consecutiveErrors = 0; systemHealthy = true; } } } export function teardown(data) { const totalDuration = Date.now() - data.startTime; console.log(`Stress test completed in ${totalDuration}ms`); console.log(`System health status: ${systemHealthy ? 'RECOVERED' : 'DEGRADED'}`); // Final health check const finalHealth = http.get(`${BASE_URL}/health`); console.log(`Final health check: ${finalHealth.status}`); if (finalHealth.status === 200) { console.log('✓ System successfully recovered from stress test'); } else { console.log('✗ System may require manual intervention'); } }