Scope:
registry-staging.aws.facetrustai.com, the real AWS Fargate deployment (0.25 vCPU / 512 MB, exactly 1 task — SQLite is single-writer, this is a hard architectural constraint, not a cost-saving default), tested with k6 against real seeded data (20 persons from the staging seed run). See FaceTrust AI API’s load-testing page for how this fits alongside the other two services’ results.Methodology
Staged ramp: 2 → 5 → 10 → 20 → 40 VUs, 20-30s per stage — deliberately the gentlest ramp of the three services tested, since this is the only one with zero redundancy (1 task, no failover) and the smallest allocated capacity. Each iteration randomly pickedGET /api/health (20%), GET /api/nin/{nin} against a real seeded NIN (40%), or GET /api/search?q=... (40%).
Results
GET /api/health and aws ecs describe-services immediately after) — no crash, no restart, just genuine overload while under-provisioned.
Not CPU or memory — SQLite/event-loop contention
CloudWatch CPUUtilization for thefacetrust-staging-registry-simulator ECS service during the test:
Even during the worst failure window, CPU peaked under 25% and memory peaked at 23% — this service was nowhere near resource-exhausted when nearly a third of its requests started failing. The far more likely explanation:
better-sqlite3’s synchronous API blocks Node’s single event loop thread for the duration of each query, and SQLite’s single-writer model serializes anything touching a write lock. Under concurrent load, requests queue up behind each other rather than genuinely running in parallel, and once that queue gets deep enough, requests are timing out or being rejected outright rather than just getting slower — the qualitative difference from api-staging and face-engine-staging, which both got much slower under load but never actually failed.
Practical implication: this isn’t fixable by giving the task more CPU or memory — 0.25 vCPU / 512 MB was never the constraint. It’s a structural consequence of the SQLite + single-task design (itself a deliberate simplification for a test/simulator service, not the real NIN/BVN registry it stands in for — see README.md’s “Swapping out the registry simulator” section). Two real Fargate services running simultaneously against a shared MySQL/Postgres backend instead of one SQLite-backed task would remove this ceiling entirely, but that’s real infrastructure work disproportionate to what a temporary test double warrants. Worth knowing the actual number (this service falls over well before either of the other two, and well before its own CPU/memory would suggest) if load testing against staging becomes routine.
What this doesn’t cover
POST /api/enroll(the write path) was not load tested — only reads. Given the single-writer bottleneck theory above, enroll load would likely show an even lower ceiling.- No sustained soak test, only a single ~2m40s ramp up and down.