Performance Test Plan
Performance Test Plan: Drop — Fintech Payment AppPlan
Project:
Drop — Remittance + QR Payments{{PROJECT_NAME}} Version:1.0{{VERSION}} Date:2026-02-23{{DATE}} Author:John (AI Director){{AUTHOR}} Status: Draft | In Review | Approved Reviewers:Alem Bašić (CEO){{REVIEWERS}}
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | Initial |
1. Performance Testing Objectives
- Validate that
Drop{{PROJECT_NAME}} 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
SQLitemaximumconcurrentcapacityuserthelimitsystem can handle beforemigratingperformanceto PostgreSQL (target: migrate at 200 concurrent)degrades - Identify bottlenecks (
bcryptdatabase,hashing,application,DB queries, rate limiter)infrastructure) 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 / (homepage) |
< |
< |
< |
< |
< |
|
POST /api/auth/login |
< |
< |
< |
< |
< |
|
GET /api/{{RESOURCE}} |
< {{P50}}ms | < {{P90}}ms | < {{P95}}ms | < {{P99}}ms | < {{ERR}}% | {{RPS}} req/s |
POST /api/ |
< |
< |
< |
< |
< |
|
query (P95) |
< |
|||||
| ||||||
| ||||||
| ||||||
| Page load (First Contentful Paint) | < |
|||||
3. Test Types
3.1 Load Testing — Normal Load Simulation
Objective: Confirm Dropthe system meets SLAs under expected normal load
(Norwegian remittance corridor peak traffic)
Normal load definition: 200{{NORMAL_USERS}} concurrent users (SQLite MVP limit) / ~20{{NORMAL_RPS}} requests/second
Duration: 10{{LOAD_DURATION}} minutes (after 2-minute ramp-up)
Pass criteria: All SLA targets in Section 2 met with ≤ 0.1%{{LOAD_ERROR_RATE}}% error rate
3.2 Stress Testing — Beyond Normal Capacity
Objective: Find SQLitethe concurrentmaximum usercapacity limit;and understand failure behavior
to plan PostgreSQL migration
Starting point: 50{{STRESS_START_USERS}} users, increasing by 25{{STRESS_INCREMENT}} users every 2 minutes{{STRESS_STEP}}min
Stop condition: Error rate > 5% or P99 > 3,000ms{{STRESS_STOP_ERROR}}% or service crashes
Pass criteria: System fails gracefully with(circuit breakers, meaningful error messages (429 rate limit, not 500);messages), data integrity maintained; no double-spend under stressmaintained
3.3 Spike Testing — Sudden Traffic Surges
Objective: SimulateVerify scenariossystem like marketing campaigns or media coverage causinghandles sudden usertraffic influxspikes without crashing
Baseline: 20{{SPIKE_BASELINE}} users
Spike to: 100{{SPIKE_PEAK}} users (5×{{SPIKE_MULTIPLIER}}× baseline)
Spike duration: 2{{SPIKE_DURATION}} minutes
Pass criteria: System recovers to baseline performance within 5{{SPIKE_RECOVERY}} minutes after spike ends; no data corruptionends
3.4 Endurance / Soak Testing — Sustained Load
Objective: Identify SQLite file lock issues, connection pool exhaustion, memoryresource leaks and degradation under sustained load
Load level: 50{{SOAK_USERS}} users (25%{{SOAK_PERCENT}}% of 200normal concurrent limit)load)
Duration: 2{{SOAK_DURATION}} hours
Metrics to watch: SQLite write queue depth, memoryMemory usage trend, response time trendtrend, disk space, database connection pool
Pass criteria: No upward trend in memory/response time over the soak period; no SQLite database locked errorsperiod
3.5 Scalability Testing — PhaseIncreasing 1Load
Objective: Verify PostgreSQLlinear migration(or enables proportionalbetter) scaling foras Phaseinfrastructure 1 target (5,000+ users)grows
Test: Step load from 200{{SCALE_START}} to 1,000 concurrent{{SCALE_MAX}} users onwhile PostgreSQLscaling stagingfrom {{MIN_INSTANCES}} to {{MAX_INSTANCES}} instances
Pass criteria: P95Throughput staysincreases underproportionally 500ms(≥ as{{SCALE_EFFICIENCY}}% concurrentefficiency) userswith scale;added no P99 explosion
Note: PostgreSQL not yet deployed — Phase 1 task. This plan establishes the baseline for comparison.instances
4. Load Profiles
| Scenario | Virtual Users | Ramp-Up | Hold | Ramp-Down | Think Time | |
|---|---|---|---|---|---|---|
| Smoke (quick sanity) | 1s | |||||
| Load ( |
||||||
| Stress |
Until |
— | ||||
| Spike |
0s | 0s | ||||
| Soak |
Think time simulation: RealisticRandom between {{THINK_MIN}}s and {{THINK_MAX}}s (realistic user think time of 2–5s (Norwegian users reviewing remittance details before confirming)behavior)
5. Test Environment
NOTE:CRITICAL: Performance tests run againston a representativededicated stagingenvironment environment.with SQLitethe MVPsame limitsinfrastructure aresizing testedas on Fly.io staging (1× shared CPU, 256MB RAM). Phase 1 PostgreSQL tests require production-equivalent environment.production.
| Component | ||
|---|---|---|
| App instances | ||
| Database | ||
| CDN |
Load generator:generator infrastructure:
- Tool:
Vitest{{PERF_TOOL}}benchmarks (api-benchmarks.test.ts) for micro-benchmarks Load generator for integration: k6 scripts ininfrastructure/performance/- Load generator location:
Local or CI runner{{LG_LOCATION}} (same region asFly.ioapp,Stockholm)separate VPC) - Load generator sizing: {{LG_SPEC}}
6. Test Data Requirements
| Data Type | Volume | Generation Method |
|---|---|---|
| Users | factory |
|
Database size at test time: ~50MB SQLite file{{DB_SIZE}}GB
Data preparation: (estimated time: npmbash run db:seed:perfscripts/perf-seed.sh~2 minutes){{SEED_TIME}}min)
7. Tools & Infrastructure
| Tool | Version | Purpose | Config |
|---|---|---|---|
|
|||
Dashboard link |
|||
link |
Script location: src/drop-app/__tests__/api-benchmarks.test.ts{{PERF_SCRIPT_PATH}} (Vitest benchmarks)
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 |
| Transactions/second | Successful |
| Data transferred | Total MB/s in + out |
Error Metrics
| Metric | Target |
|---|---|
| HTTP error rate (5xx) | < |
| Connection |
Resource Utilization (Fly.io Metrics)
| Resource | Warning | Critical |
|---|---|---|
| App CPU | > |
> |
| App Memory | > |
> |
| > |
> |
|
| DB Connections | > {{DB_CONN_WARN}}% of pool | > {{DB_CONN_CRIT}}% |
| Cache hit ratio | < {{CACHE_HIT}}% | < {{CACHE_CRIT}}% |
Database Query Performance (NFR-P04)
| Metric | Target |
|---|---|
| < | |
| Slow queries (> |
< {{SLOW_COUNT}} per minute |
| Deadlocks | 0 |
9. SLA Targets Per Endpoint
| Endpoint | Method | P95 SLA | Error Rate SLA | Notes |
|---|---|---|---|---|
/ |
GET | {{P95}}ms | < {{ERR}}% | Static or cached |
/api/auth/login |
POST | < |
||
/api/ |
< |
|||
/api/ |
POST | < |
||
| ||||
| ||||
| ||||
10. Baseline Establishment
Baseline established:criteria: PhaseSystem 0in MVPstable onstate, localseeded devwith (SQLiteproduction-realistic in-memory)data, running load test scenario for {{BASELINE_DURATION}} minutes
Baseline metrics to record:
| Metric | Baseline Value | Date Recorded | |
|---|---|---|---|
Regression threshold: Alert if any metric degrades > 15%{{REGRESSION_THRESHOLD}}% vs baseline
11. Test Execution Schedule
| Run Type | Trigger | Environment | Frequency |
|---|---|---|---|
| Every |
Per |
||
| Release candidate | Per release | ||
| Quarterly | |||
| Before |
|||
| Scalability | Infrastructure changes | Production-sized | As needed |
12. Results Analysis Template
Test Run ID: {{RUN_ID}}
Date: {{DATE}}
Tester: Builder Agent + Validator Agent{{TESTER}}
Scenario: Load (normal) / Stress / Spike / Soak{{SCENARIO}}
Build / Version: v{{{VERSION}}
| Endpoint | P50 (ms) | P90 (ms) | P95 (ms) | P99 (ms) | Error % | RPS | Status vs SLA |
|---|---|---|---|---|---|---|---|
{{ENDPOINT}} |
— | — | — | — | — | — | ✅ Pass / |
| |||||||
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:{LIMIT} users{FINDING_1}}Recommend{{FINDING_2}}
Comparison at:to baseline:
- {
TRIGGER} concurrent users (per NFR-S02){COMPARISON}}
Recommendation: Pass / Fail / Conditional pass with PostgreSQL migration required for Phase 1{{CONDITIONS}}
13. Bottleneck Identification Process
Drop-specific bottleneck investigation order:
1. Check bcryptapplication timingerror rate → bcryptIs >the 1,000msapp P95returning =errors, configor issuejust (rounds too high OR long password pre-check missing)slow?
2. Check SQLiteP99 writevs queueP50 spread → "databaseLarge is locked" errorsspread = concurrentoutlier writerequests saturation(DB →queries, triggerlocks, PostgreSQLGC migrationpauses)
3. Check rateCPU limiter 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 metricssaturation → CPU > 90%80% sustained = scalecompute upbottleneck
instance;4. Check memory → Memory >growing 200MBduring test = Node.js leak check/ 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 loss
14. Remediation Tracking
| Issue | Found In | Severity | Root Cause | Fix | Fixed In | Verified |
|---|---|---|---|---|---|---|
Related Documents
- Test Strategy
Non-FunctionalMonitoringRequirements& ObservabilityTestingSLAGuideTest InventoryReport
Approval
| Role | Name | Date | Signature |
|---|---|---|---|
| Author | |||