Skip to main content

Performance Test Plan

Performance Test Plan: Drop — Fintech Payment App

Project: Drop — Remittance + QR Payments Version: 1.0 Date: 2026-02-23 Author: John (AI Director) Status: Approved Reviewers: Alem Bašić (CEO)

Document History

Version Date Author Changes
0.1 2026-02-23 John Initial performance plan — targets from NFR-P01..P06 + api-benchmarks.test.ts

1. Performance Testing Objectives

  1. Validate that 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)
  2. Determine the SQLite concurrent user limit before migrating to PostgreSQL (target: migrate at 200 concurrent)
  3. Identify bottlenecks (bcrypt hashing, DB queries, rate limiter) before production release
  4. Establish a performance baseline for regression comparison in future releases

Reference NFRs: docs/BUSINESS-REQUIREMENTS/non-functional-requirements.md — Section 2 (Performance)


2. Performance Requirements Reference

Endpoint / Feature P50 P90 P95 P99 Error Rate Notes
GET /api/health < 20ms < 50ms < 100ms < 200ms < 0.1% Health check — fast always
POST /api/auth/login (bcrypt) < 700ms < 900ms < 1,000ms < 1,200ms < 0.1% bcrypt 12 rounds; NFR-P03
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/rates < 20ms < 50ms < 100ms < 200ms < 0.1% Cached / fast
GET /api/transactions < 50ms < 100ms < 200ms < 400ms < 0.1% NFR-P02
DB SELECT query < 10ms 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) < 3,000ms 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 Drop meets SLAs under expected normal load (Norwegian remittance corridor peak traffic) Normal load definition: 200 concurrent users (SQLite MVP limit) / ~20 requests/second Duration: 10 minutes (after 2-minute ramp-up) Pass criteria: All SLA targets in Section 2 met with ≤ 0.1% error rate

3.2 Stress Testing — Beyond Normal Capacity

Objective: Find SQLite concurrent user limit; understand failure behavior to plan PostgreSQL migration Starting point: 50 users, increasing by 25 users every 2 minutes Stop condition: Error rate > 5% or P99 > 3,000ms or service crashes Pass criteria: System fails gracefully with meaningful error messages (429 rate limit, not 500); data integrity maintained; no double-spend under stress

3.3 Spike Testing — Sudden Traffic Surges

Objective: Simulate scenarios like marketing campaigns or media coverage causing sudden user influx Baseline: 20 users Spike to: 100 users (5× baseline) Spike duration: 2 minutes Pass criteria: System recovers to baseline performance within 5 minutes after spike ends; no data corruption

3.4 Endurance / Soak Testing — Sustained Load

Objective: Identify SQLite file lock issues, connection pool exhaustion, memory leaks under sustained load Load level: 50 users (25% of 200 concurrent limit) Duration: 2 hours Metrics to watch: SQLite write queue depth, memory usage trend, response time trend Pass criteria: No upward trend in response time over soak period; no SQLite database locked errors

3.5 Scalability Testing — Phase 1 PostgreSQL Migration

Objective: Verify PostgreSQL migration enables proportional scaling for Phase 1 target (5,000+ users) Test: Step load from 200 to 1,000 concurrent users on PostgreSQL staging Pass criteria: P95 stays under 500ms as concurrent users scale; no 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 Notes
Smoke (quick sanity) 5 10s 1min 10s 1s Per-deploy quick check
Load (normal SQLite) 200 2min 10min 1min 2s NFR-S01 MVP target
Stress (find limit) Up to 500 2min steps Until fail 2s SQLite vs PostgreSQL
Spike (traffic burst) 100 (instant) 0s 2min 0s 1s Marketing/media event
Soak (stability) 50 1min 2h 2min 3s Memory leak detection

Think time simulation: Realistic user think time of 2–5s (Norwegian users reviewing remittance details before confirming)


5. Test Environment

NOTE: Performance tests run against a representative staging environment. SQLite MVP limits are tested on Fly.io staging (1× shared CPU, 256MB RAM). Phase 1 PostgreSQL tests require production-equivalent environment.

Component MVP Staging (Fly.io) Phase 1 Target
App instances 1× shared-cpu-1x (256MB) 2× dedicated-cpu-2x
Database SQLite on persistent volume PostgreSQL (managed Fly.io)
Region Stockholm (arn) Stockholm (arn)
CDN None (API only) None (API) / Vercel (landing)

Load generator:

  • Tool: Vitest benchmarks (api-benchmarks.test.ts) for micro-benchmarks
  • Load generator for integration: k6 scripts in infrastructure/performance/
  • Load generator location: Local or CI runner (same region as Fly.io Stockholm)

6. Test Data Requirements

Data Type Volume Generation Method
Users 500 npm run db:seed:perf
Recipients (remittance targets) 200 Seed script
Merchants 50 Seed script
Transactions (historical) 10,000 Bulk insert seed
Exchange rates 6 corridors (fixed) Always seeded

Database size at test time: ~50MB SQLite file Data preparation: npm run db:seed:perf (estimated time: ~2 minutes)


7. Tools & Infrastructure

Tool Version Purpose Config
Vitest (bench) 2.x Micro-benchmarks: bcrypt, DB queries, rate limiter src/drop-app/__tests__/api-benchmarks.test.ts
k6 Latest HTTP load testing (concurrent users) infrastructure/performance/k6-scripts/
Fly.io Metrics Real-time CPU/memory/request metrics Fly.io dashboard
SQLite EXPLAIN QUERY PLAN DB query analysis Via flyctl ssh console

Script location: src/drop-app/__tests__/api-benchmarks.test.ts (Vitest benchmarks)


8. Key Metrics to Capture

Response Time

Metric Description Tool
P50 (median) Half of requests faster than this Vitest bench / k6
P90 90% of requests faster than this k6
P95 95% of requests faster than this k6 (NFR gate)
P99 99% of requests faster than this k6
Max Worst single request k6

Throughput

Metric Description
Requests/second Total API throughput at peak load
Transactions/second Successful remittance/QR transactions per second

Error Metrics

Metric Target
HTTP error rate (5xx) < 0.1%
SQLite database locked errors 0% (rate limit blocks excess concurrency)
Connection timeout rate < 0.1%

Resource Utilization (Fly.io Metrics)

Resource Warning Critical
App CPU > 70% > 90%
App Memory > 80% (200MB / 256MB) > 95%
SQLite write queue > 50ms wait > 200ms wait

Database Query Performance (NFR-P04)

Metric Target
SELECT query P95 < 10ms
INSERT query P95 < 20ms
Slow queries (> 100ms) 0 per minute under normal load

9. SLA Targets Per Endpoint (from api-benchmarks.test.ts)

Endpoint Method P95 SLA Error Rate SLA Notes
/api/auth/login POST 1,000ms < 0.1% bcrypt 12 rounds
/api/auth/register POST 1,000ms < 0.1% bcrypt 12 rounds
/api/transactions/remittance POST 500ms < 0.1% NFR-P02
/api/transactions/qr-payment POST 500ms < 0.1% NFR-P02
/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 established: Phase 0 MVP on local dev (SQLite in-memory)

Metric Baseline Value Date Recorded Test
bcrypt hash (register) ~800ms 2026-02-23 api-benchmarks.test.ts
bcrypt verify (login) ~800ms 2026-02-23 api-benchmarks.test.ts
Rate limit check (50 concurrent) ~1,800ms 2026-02-23 api-benchmarks.test.ts
DB SELECT ~5ms 2026-02-23 api-benchmarks.test.ts
DB INSERT ~10ms 2026-02-23 api-benchmarks.test.ts
SHA-256 hash (reject baseline) ~1ms 2026-02-23 auth.test.ts

Regression threshold: Alert if any metric degrades > 15% vs baseline


11. Test Execution Schedule

Run Type Trigger Environment Frequency
Benchmark smoke Every CI run Local / CI runner Per commit
api-benchmarks.test.ts full Every PR CI Per PR
k6 load test Release candidate Fly.io staging Per release
k6 stress test Before Phase 1 PostgreSQL migration Fly.io staging Quarterly
k6 soak test Before Phase 1 launch Fly.io staging Pre-launch

12. Results Analysis Template

Test Run ID: {RUN_ID} Date: {DATE} Tester: Builder Agent + Validator Agent 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 Pass / Fail
/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: {LIMIT} users
  • Recommend PostgreSQL migration at: {TRIGGER} concurrent users (per NFR-S02)

Recommendation: Pass / Fail / Conditional pass with PostgreSQL migration required for Phase 1


13. Bottleneck Identification Process

Drop-specific bottleneck investigation order:

1. Check bcrypt timing → bcrypt > 1,000ms P95 = config issue (rounds too high OR long password pre-check missing)
2. Check SQLite write queue → "database is locked" errors = concurrent write saturation → trigger PostgreSQL migration
3. Check rate 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 metrics → CPU > 90% = scale up instance; Memory > 200MB = Node.js leak check

14. Remediation Tracking

Issue Found In Severity Root Cause Fix Fixed In Verified
In-memory rate limiter reset on restart Phase 0 load test High In-memory Map → DB-backed Phase 0.5 security hardening v0.5.0 db.test.ts
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


Approval

Role Name Date Signature
Author John (AI Director) 2026-02-23 Approved (AI)
QA Lead Validator Agent 2026-02-23 Approved (AI)
AI Director (John) John 2026-02-23 Approved
CEO (Alem) Alem Bašić TBD