Load Testing & Backup
k6 performance test suites with custom metrics, SLA thresholds, CI/CD pipeline integration, and multi-provider backup/DR strategy with actual recovery commands.
SCRIPE includes enterprise-grade k6 load testing scripts that validate performance SLAs under realistic workloads using custom SCRIPE-specific metrics. Combined with a comprehensive backup and disaster recovery strategy covering SQL Server, Oracle, PostgreSQL, and Redis — including actual recovery commands — the platform ensures both performance confidence and data durability across all deployment environments.
k6 Test Suites
Two pre-built k6 test suites cover the critical user journeys: authentication flows (login, JWT retrieval, protected endpoints, health checks) and CRUD operations (pagination, filtering, spike scenarios). Each suite defines VU ramp stages and custom metrics tracked in Grafana.
| Test Suite | File | Stages | What It Tests |
|---|---|---|---|
| Auth Flow | tests/load/auth-flow.js | 2m ramp → 50 VUs × 5m → 1m ramp-down | Login → JWT retrieval → protected endpoints → health check → concurrent sessions |
| CRUD Operations | tests/load/crud-operations.js | 2m ramp → 30 VUs × 5m → 1m ramp-down | Create → Read (paginated) → Update → Delete with spike scenarios |
SLA Thresholds
| Metric | Threshold | Description | SLA Impact |
|---|---|---|---|
| http_req_duration (P95) | < 2000ms | 95% of requests must complete within 2 seconds | Pipeline FAILS if breached |
| http_req_duration (P99) | < 5000ms | 99% of requests must complete within 5 seconds | Pipeline FAILS if breached |
| http_req_failed | < 1% | Less than 1% of requests may return errors | Pipeline FAILS if breached |
| http_req_duration (avg) | < 500ms | Average response time under 500ms | Pipeline FAILS if breached |
| scripe_login_duration (P95) | < 3000ms | 95% of login requests complete in 3 seconds | Auth-specific SLA |
| scripe_login_fail_rate | < 5% | Less than 5% of login attempts may fail | Auth reliability SLA |
Auth Flow Test Script
The auth-flow.js test simulates realistic user authentication patterns: login with credentials, access a protected endpoint with the JWT token, and verify the health check endpoint. Custom metrics (scripe_login_duration, scripe_login_fail_rate) track auth-specific SLAs independently from general HTTP metrics.
import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Rate, Trend } from 'k6/metrics';
// ── Custom Metrics (visible in k6 output + Grafana) ──
const loginDuration = new Trend('scripe_login_duration', true);
const loginFailRate = new Rate('scripe_login_fail_rate');
const tokenRefreshDuration = new Trend('scripe_token_refresh_duration', true);
// ── Environment Configuration ──
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5035';
const ADMIN_EMAIL = __ENV.ADMIN_EMAIL || 'superadmin@scripe.com';
const ADMIN_PASSWORD = __ENV.ADMIN_PASSWORD || 'Admin@123';
/**
* Exported constant defining parameters and fields for options configurations.
*/
export const options = {
stages: [
{ duration: '2m', target: 50 }, // Ramp up to 50 concurrent users
{ duration: '5m', target: 50 }, // Sustain load for 5 minutes
{ duration: '1m', target: 0 }, // Ramp down gracefully
],
thresholds: {
'http_req_duration': ['p(95)<2000', 'p(99)<5000'],
'scripe_login_duration': ['p(95)<3000'],
'scripe_login_fail_rate': ['rate<0.05'],
'http_req_failed': ['rate<0.01'],
},
};
// Test Flow: Login → Protected Endpoint → Health Check
/**
* Exported function in the docs module.
*/
export default function () {
group('1. Login', () => { /* POST /api/auth/login */ });
group('2. Access Protected Endpoint', () => { /* GET /api/admins/current */ });
group('3. Health Check', () => { /* GET /health/ready */ });
sleep(Math.random() * 2 + 1); // Random 1-3s think time
}Running Load Tests
# ═══════════════════════════════════════════════════════════
# INSTALL k6
# ═══════════════════════════════════════════════════════════
# Windows: winget install k6 (or choco install k6)
# macOS: brew install k6
# Linux: sudo apt install k6
# Docker: docker run --rm -i grafana/k6 run - <script.js
# ═══════════════════════════════════════════════════════════
# DEVELOPMENT (local backend)
# ═══════════════════════════════════════════════════════════
# Start your backend first: dotnet run (or scripe dev backend)
k6 run tests/load/auth-flow.js
k6 run tests/load/crud-operations.js
# ═══════════════════════════════════════════════════════════
# DOCKER COMPOSE (containerized backend)
# ═══════════════════════════════════════════════════════════
k6 run tests/load/auth-flow.js \
--env BASE_URL=http://host.docker.internal:5035
# ═══════════════════════════════════════════════════════════
# STAGING / PRODUCTION (remote server)
# ═══════════════════════════════════════════════════════════
k6 run tests/load/auth-flow.js \
--env BASE_URL=https://api.staging.scripe.com \
--env ADMIN_EMAIL=staging-admin@scripe.com \
--env ADMIN_PASSWORD=StrongP@ss123
# ═══════════════════════════════════════════════════════════
# CUSTOM PARAMETERS
# ═══════════════════════════════════════════════════════════
# Override VUs and duration for quick smoke tests
k6 run tests/load/auth-flow.js -u 10 --duration 30s
# Heavy load test (100 concurrent users, 10 minutes)
k6 run tests/load/auth-flow.js -u 100 --duration 10m
# Output to JSON for CI/CD analysis
k6 run tests/load/auth-flow.js --out json=results.json
# Output to Prometheus for Grafana dashboards
k6 run tests/load/auth-flow.js \
--out experimental-prometheus-rw \
--env K6_PROMETHEUS_RW_SERVER_URL=http://localhost:9090/api/v1/writeCI/CD Integration
k6 integrates into GitHub Actions, GitLab CI, and Azure Pipelines. Tests run against a containerized backend instance with health readiness wait before execution. The pipeline fails automatically if any SLA threshold is breached. Results are uploaded as artifacts for trend analysis.
name: Load Tests
on:
schedule:
- cron: '0 3 * * 1' # Every Monday at 3 AM
workflow_dispatch: # Manual trigger
jobs:
load-test:
runs-on: ubuntu-latest
services:
api:
image: scripe-api:latest
ports: ['5035:5035']
env:
ASPNETCORE_ENVIRONMENT: Staging
Database__Provider: SqlServer
Database__ConnectionString: ${{ secrets.TEST_DB_CONNECTION }}
steps:
- uses: actions/checkout@v4
- name: Wait for API readiness
run: |
for i in {1..30}; do
curl -sf http://localhost:5035/health/ready && break
sleep 5
done
- name: Run Auth Flow Test
uses: grafana/k6-action@v0.3.1
with:
filename: tests/load/auth-flow.js
flags: >-
--env BASE_URL=http://localhost:5035
--env ADMIN_EMAIL=${{ secrets.TEST_ADMIN_EMAIL }}
--env ADMIN_PASSWORD=${{ secrets.TEST_ADMIN_PASSWORD }}
--out json=auth-results.json
- name: Run CRUD Operations Test
uses: grafana/k6-action@v0.3.1
with:
filename: tests/load/crud-operations.js
flags: >-
--env BASE_URL=http://localhost:5035
--env ADMIN_EMAIL=${{ secrets.TEST_ADMIN_EMAIL }}
--env ADMIN_PASSWORD=${{ secrets.TEST_ADMIN_PASSWORD }}
- name: Upload Results
uses: actions/upload-artifact@v4
if: always()
with:
name: k6-results
path: auth-results.jsonBackup & Disaster Recovery
SCRIPE supports multi-provider backup strategies with specific tools, frequencies, and recovery commands tailored to each database engine. The backup strategy ensures compliance with enterprise RPO (Recovery Point Objective) and RTO (Recovery Time Objective) requirements. Audit logs have a separate backup with extended retention for compliance.
| Component | Strategy | Frequency | Retention | Recovery Tool |
|---|---|---|---|---|
| SQL Server | Full + Differential + Transaction Log | Daily / Hourly / 15min | 30 days | SSMS Restore or T-SQL RESTORE DATABASE |
| Oracle | RMAN Full + Incremental + Archive Log | Weekly / Daily / Continuous | 30 days | RMAN RECOVER + RESTORE |
| PostgreSQL | pg_dump + WAL archiving | Daily + Continuous | 30 days | pg_restore + Point-in-Time Recovery |
| Redis | RDB snapshots + AOF persistence | Hourly + Real-time | 7 days | redis-cli --rdb / AOF replay |
| File Storage | Cloud provider snapshots or rsync | Daily | 90 days | Cloud console restore or rsync reverse |
| Audit Logs | Separate backup (compliance requirement) | Daily | 1 year minimum | SQL restore to read-only replica |
# ═══════════════════════════════════════════════════════════
# SQL SERVER BACKUP (Windows / Docker)
# ═══════════════════════════════════════════════════════════
# Full backup
sqlcmd -S localhost -U sa -P 'YourPassword' -Q \
"BACKUP DATABASE SCRIPE TO DISK='/backups/scripe_full.bak'"
# Point-in-time restore
sqlcmd -S localhost -U sa -P 'YourPassword' -Q \
"RESTORE DATABASE SCRIPE FROM DISK='/backups/scripe_full.bak' \
WITH STOPAT='2026-04-06T20:00:00'"
# ═══════════════════════════════════════════════════════════
# POSTGRESQL BACKUP (Linux / Docker)
# ═══════════════════════════════════════════════════════════
# Full dump
pg_dump -h localhost -U scripe -d scripe_db -F c > scripe_backup.dump
# Restore
pg_restore -h localhost -U scripe -d scripe_db scripe_backup.dump
# ═══════════════════════════════════════════════════════════
# REDIS BACKUP
# ═══════════════════════════════════════════════════════════
# Trigger RDB snapshot
redis-cli BGSAVE
# Copy RDB file
cp /var/lib/redis/dump.rdb /backups/redis_$(date +%Y%m%d).rdb