Performance Test Plan
Performance Test PlanPlan: Drop — Fintech Payment App
Project:
{{PROJECT_NAME}}Drop — Remittance + QR Payments Version:{{VERSION}}1.0 Date:{{DATE}}2026-02-23 Author:{{AUTHOR}}John (AI Director) Status:Draft | In Review |Approved Reviewers:{{REVIEWERS}}Alem Bašić (CEO)
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | Initial |
1. Performance Testing Objectives
- Validate that
{{PROJECT_NAME}}Drop meets the performance SLAs defined in the NFR document under normal operating conditions (200 concurrent users on SQLite MVP; 5,000+ on PostgreSQL Phase 1) - Determine the
maximumSQLitecapacityconcurrenttheusersystem can handlelimit beforeperformancemigratingdegradesto PostgreSQL (target: migrate at 200 concurrent) - Identify bottlenecks (
database,bcryptapplication,hashing,infrastructure)DB queries, rate limiter) before production release - Establish a performance baseline for regression comparison in future releases
Reference NFRs: {{NFR_DOC_LINK}}docs/BUSINESS-REQUIREMENTS/non-functional-requirements.md — Section 2 (Performance)
2. Performance Requirements Reference
| Endpoint / Feature | P50 | P90 | P95 | P99 | Error Rate | |
|---|---|---|---|---|---|---|
GET /api/health |
< |
< |
< |
< |
< |
|
POST /api/auth/login (bcrypt) |
< |
< |
< |
< |
< |
|
POST /api/auth/register (bcrypt) |
< 700ms | < 900ms | < 1,000ms | < 1,200ms | < 0.1% | Same bcrypt cost |
POST /api/transactions/remittance |
< 200ms | < 350ms | < 500ms | < 800ms | < 0.1% | NFR-P02 |
POST /api/transactions/qr-payment |
< 200ms | < 350ms | < 500ms | < 800ms | < 0.1% | NFR-P02 |
GET /api/ |
< |
< |
< |
< |
< |
|
|
< |
< |
< |
< |
< |
|
| — | — | < |
— | 0% | NFR-P04 | |
| DB INSERT query | — | — | < 20ms | — | 0% | NFR-P04 |
| 50 concurrent rate limit checks | — | — | — | — | 0% | < 2,000ms total (NFR-P06) |
| Page load (First Contentful Paint) | — | — | < |
— | — | NFR-P01 (4G, cold cache) |
| Core Web Vitals LCP | — | — | < 2,500ms | — | — | NFR-P05 |
3. Test Types
3.1 Load Testing — Normal Load Simulation
Objective: Confirm the systemDrop meets SLAs under expected normal load (Norwegian remittance corridor peak traffic)
Normal load definition: {{NORMAL_USERS}}200 concurrent users (SQLite MVP limit) / {{NORMAL_RPS}}~20 requests/second
Duration: {{LOAD_DURATION}}10 minutes (after 2-minute ramp-up)
Pass criteria: All SLA targets in Section 2 met with ≤ {{LOAD_ERROR_RATE}}%0.1% error rate
3.2 Stress Testing — Beyond Normal Capacity
Objective: Find theSQLite maximumconcurrent capacityuser andlimit; understand failure behavior to plan PostgreSQL migration
Starting point: {{STRESS_START_USERS}}50 users, increasing by {{STRESS_INCREMENT}}25 users every {{STRESS_STEP}}min2 minutes
Stop condition: Error rate > {{STRESS_STOP_ERROR}}%5% or P99 > 3,000ms or service crashes
Pass criteria: System fails gracefully (circuit breakers,with meaningful error messages),messages (429 rate limit, not 500); data integrity maintainedmaintained; no double-spend under stress
3.3 Spike Testing — Sudden Traffic Surges
Objective: VerifySimulate systemscenarios handleslike marketing campaigns or media coverage causing sudden trafficuser spikes without crashinginflux
Baseline: {{SPIKE_BASELINE}}20 users
Spike to: {{SPIKE_PEAK}}100 users ({{SPIKE_MULTIPLIER}}×5× baseline)
Spike duration: {{SPIKE_DURATION}}2 minutes
Pass criteria: System recovers to baseline performance within {{SPIKE_RECOVERY}}5 minutes after spike endsends; no data corruption
3.4 Endurance / Soak Testing — Sustained Load
Objective: Identify resourceSQLite file lock issues, connection pool exhaustion, memory leaks and degradation under sustained load
Load level: {{SOAK_USERS}}50 users ({{SOAK_PERCENT}}%25% of normal200 load)concurrent limit)
Duration: {{SOAK_DURATION}}2 hours
Metrics to watch: MemorySQLite write queue depth, memory usage trend, response time trend, disk space, database connection pooltrend
Pass criteria: No upward trend in memory/response time over the soak periodperiod; no SQLite database locked errors
3.5 Scalability Testing — IncreasingPhase Load1 PostgreSQL Migration
Objective: Verify linearPostgreSQL (ormigration better)enables proportional scaling asfor infrastructurePhase grows1 target (5,000+ users)
Test: Step load from {{SCALE_START}}200 to {{SCALE_MAX}}1,000 concurrent users whileon scalingPostgreSQL from {{MIN_INSTANCES}} to {{MAX_INSTANCES}} instancesstaging
Pass criteria: ThroughputP95 increasesstays proportionallyunder (≥500ms {{SCALE_EFFICIENCY}}%as efficiency)concurrent withusers addedscale; instancesno P99 explosion
Note: PostgreSQL not yet deployed — Phase 1 task. This plan establishes the baseline for comparison.
4. Load Profiles
| Scenario | Virtual Users | Ramp-Up | Hold | Ramp-Down | Think Time | |
|---|---|---|---|---|---|---|
| Smoke (quick sanity) | 1s | |||||
| Load ( |
||||||
| Stress (find limit) | Until |
— | ||||
| Spike (traffic burst) | 0s | 0s | ||||
| Soak (stability) |
Think time simulation: Random between {{THINK_MIN}}s and {{THINK_MAX}}s (realisticRealistic user behavior)think time of 2–5s (Norwegian users reviewing remittance details before confirming)
5. Test Environment
CRITICAL:NOTE: Performance tests run onagainst a dedicatedrepresentative environmentstaging withenvironment. theSQLite sameMVP infrastructurelimits sizingare astested production.on Fly.io staging (1× shared CPU, 256MB RAM). Phase 1 PostgreSQL tests require production-equivalent environment.
| Component | ||
|---|---|---|
| App instances | ||
| Database | ||
| CDN |
Load generator infrastructure:generator:
- Tool:
{{PERF_TOOL}}Vitest benchmarks (api-benchmarks.test.ts) for micro-benchmarks - Load generator for integration: k6 scripts in
infrastructure/performance/ - Load generator location:
{{LG_LOCATION}}Local or CI runner (same region asapp,Fly.ioseparate VPC) Load generator sizing: {{LG_SPEC}}Stockholm)
6. Test Data Requirements
| Data Type | Volume | Generation Method |
|---|---|---|
| Users | npm |
|
| Exchange rates | 6 corridors (fixed) | Always seeded |
Database size at test time: {{DB_SIZE}}GB~50MB SQLite file
Data preparation: (estimated time: bashnpm scripts/perf-seed.shrun db:seed:perf{{SEED_TIME}}min)~2 minutes)
7. Tools & Infrastructure
| Tool | Version | Purpose | Config |
|---|---|---|---|
|
|||
infrastructure/performance/k6-scripts/ |
|||
flyctl ssh console |
Script location: (Vitest benchmarks){{PERF_SCRIPT_PATH}}src/drop-app/__tests__/api-benchmarks.test.ts
8. Key Metrics to Capture
Response Time
| Metric | Description | Tool |
|---|---|---|
| P50 (median) | Half of requests faster than this | |
| P90 | 90% of requests faster than this | |
| P95 | 95% of requests faster than this | |
| P99 | 99% of requests faster than this | |
| Max | Worst single request |
Throughput
| Metric | Description |
|---|---|
| Requests/second | Total API throughput at peak load |
| Transactions/second | Successful |
Error Metrics
| Metric | Target |
|---|---|
| HTTP error rate (5xx) | < |
| Connection |
Resource Utilization (Fly.io Metrics)
| Resource | Warning | Critical |
|---|---|---|
| App CPU | > |
> |
| App Memory | > |
> |
| > |
> | 200ms |
Database Query Performance (NFR-P04)
| Metric | Target |
|---|---|
| < |
|
| INSERT query P95 | < 20ms |
| Slow queries (> |
|
9. SLA Targets Per Endpoint (from api-benchmarks.test.ts)
| Endpoint | Method | P95 SLA | Error Rate SLA | Notes |
|---|---|---|---|---|
/api/auth/login |
< |
|||
/api/auth/ |
POST | < |
||
/api/ |
< |
|||
/api/ |
POST | < |
||
/api/rates |
GET | 100ms | < 0.1% | Cached |
/api/health |
GET | 100ms | < 0.1% | Always fast |
| DB SELECT | — | 10ms | 0% | NFR-P04 |
| DB INSERT | — | 20ms | 0% | NFR-P04 |
| 50 concurrent rate limit | — | 2,000ms total | 0% | NFR-P06 |
10. Baseline Establishment (api-benchmarks.test.ts Results)
Baseline criteria:established: SystemPhase in0 stableMVP state,on seededlocal withdev production-realistic(SQLite data, running load test scenario for {{BASELINE_DURATION}} minutes
Baseline metrics to record:in-memory)
| Metric | Baseline Value | Date Recorded | Test |
|---|---|---|---|
| api-benchmarks.test.ts | |||
| api-benchmarks.test.ts | |||
| api-benchmarks.test.ts | |||
| api-benchmarks.test.ts | |||
| api-benchmarks.test.ts | |||
| SHA-256 hash (reject baseline) | ~1ms | 2026-02-23 | auth.test.ts |
Regression threshold: Alert if any metric degrades > {{REGRESSION_THRESHOLD}}%15% vs baseline
11. Test Execution Schedule
| Run Type | Trigger | Environment | Frequency |
|---|---|---|---|
| Every |
Per |
||
| Every PR | CI | Per PR | |
| k6 load test | Release candidate | Per release | |
| Quarterly | |||
| Before |
|||
12. Results Analysis Template
Test Run ID: {{RUN_ID}}
Date: {{DATE}}
Tester: {{TESTER}}Builder Agent + Validator Agent
Scenario: {{SCENARIO}}Load (normal) / Stress / Spike / Soak
Build / Version: {{v{VERSION}}
| Endpoint | P50 (ms) | P90 (ms) | P95 (ms) | P99 (ms) | Error % | RPS | Status vs SLA |
|---|---|---|---|---|---|---|---|
/api/auth/login |
— | — | — | — | — | — | |
/api/transactions/remittance |
— | — | — | — | — | — | Pass / Fail |
| DB SELECT | — | — | — | — | — | — | Pass / Fail |
Summary:
- Peak concurrent users: {
{PEAK_USERS}} - Peak throughput: {
{PEAK_RPS}}req/s - Test duration: {
{DURATION}}min - Total requests: {
{TOTAL_REQUESTS}} - Total errors: {
{TOTAL_ERRORS}}
Notable findings:
- SQLite concurrent user limit reached at: {
{FINDING_1}}LIMIT} users - Recommend PostgreSQL migration at: {
{FINDING_2}}TRIGGER}
Comparisonusers to(per baseline:
{{COMPARISON}}NFR-S02)
Recommendation: Pass / Fail / Conditional pass with {{CONDITIONS}}PostgreSQL migration required for Phase 1
13. Bottleneck Identification Process
Drop-specific bottleneck investigation order:
1. Check applicationbcrypt error ratetiming → Isbcrypt the> app1,000ms returningP95 errors,= orconfig justissue slow?(rounds too high OR long password pre-check missing)
2. Check P99SQLite vswrite P50 spreadqueue → Large"database spreadis locked" errors = outlierconcurrent requestswrite (DBsaturation queries,→ locks,trigger GCPostgreSQL pauses)migration
3. Check CPUrate saturationlimiter DB table → rate_limit_requests table query slow = add index or migrate to Redis
4. Check transaction lock → SELECT FOR UPDATE timeout = deadlock in double-spend prevention
5. Check mock BaaS response → mock service timeout = CI/staging environment issue
6. Check Fly.io metrics → CPU > 80% sustained90% = computescale bottleneckup 4. Check memory →instance; Memory growing> during test200MB = Node.js leak / GC pressure
5. Check DB metrics → Slow queries, high connections, deadlocks
6. Check cache hit rate → Low cache hit = DB overloaded unnecessarily
7. Check external calls → Third-party API latency or rate limiting
8. Check network → Bandwidth saturation, packet losscheck
14. Remediation Tracking
| Issue | Found In | Severity | Root Cause | Fix | Fixed In | Verified |
|---|---|---|---|---|---|---|
| Long password bcrypt DoS (10KB) | Phase 0 security audit | High | No max length before bcrypt | 1,000 char limit in validation | v0.5.0 | validation.test.ts |
Related Documents
- Test Strategy
MonitoringNon-Functional& ObservabilityRequirementsSLATestingReportGuide- Test Inventory
Approval
| Role | Name | Date | Signature |
|---|---|---|---|
| Author | John (AI Director) | 2026-02-23 | Approved (AI) |
| Validator Agent | 2026-02-23 | Approved (AI) | |
| John | 2026-02-23 | Approved | |
| CEO (Alem) | Alem Bašić | TBD |