Skip to content

OpenBankProject/OBP-load-tester

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OBP Load Tester

Self-contained Go load tester for the Open Bank Project API. Creates its own user(s), bank, accounts, views, and consents, then exercises a broad mix of endpoints across API versions — including a dedicated v7.0.0 sweep designed to stress the http4s request-scoped-connection design and surface pool starvation or deadlock-like behaviour.

TL;DR

# 1. Set OBP_CONSUMER_KEY in .env (copy .env.example first)
cp .env.example .env
$EDITOR .env

# 2. Basic smoke run — auto-creates a user, runs everything with defaults
./run.sh

# 3. v7.0.0 only, fast — skip accounts/consents, one worker, 5 loops
./run.sh --loop-accounts 0 --loop-consents 0 --loop-v7 5

# 4. Concurrent v7 sweep — 40 workers, 10 distinct users, 20 loops each
./run.sh --users-v7 10 --workers-v7 40 --loop-v7 20 \
         --loop-accounts 0 --loop-consents 0

# 5. Point at a non-default host
./run.sh --host https://api.example.com --consumer MYKEY

# 6. See all flags
./run.sh --help

Full stdout is tee'd to go-app/last_run.log. Concurrent worker lines are prefixed [wN]. The v7 sweep ends with a per-endpoint latency table and a pass/fail verdict flagging pool-starvation signatures.

Prerequisites

  1. Copy .env.example to .env and set OBP_CONSUMER_KEY (and optionally OBP_API_HOST, OBP_USERNAME, OBP_PASSWORD).
  2. Run ./run.sh — it auto-creates a throwaway user if no username/password is supplied.

Flags

All flags are long-form. Values can also come from environment variables (.env is auto-sourced if present). Flags override env.

Flag Env var Default Description
--consumer KEY OBP_CONSUMER_KEY — (required) Consumer key for DirectLogin
--host URL OBP_API_HOST http://127.0.0.1:8080 OBP API host including protocol and port
--username NAME OBP_USERNAME auto-created DirectLogin username
--password PWD OBP_PASSWORD auto-created DirectLogin password
--loop-accounts N OBP_LOOP_ACCOUNTS 3 Number of account-creation loops
--loop-consents N OBP_LOOP_CONSENTS 3 Number of consent create/use/revoke loops
--loop-v7 N OBP_LOOP_V7 3 Number of times each worker sweeps v7.0.0 endpoints (0 to skip)
--workers-v7 N OBP_WORKERS_V7 1 Concurrent goroutines for the v7.0.0 sweep
--users-v7 N OBP_USERS_V7 1 Distinct users to spread v7 workers across
--help Show usage

What gets exercised

Setup phase (serial, once per run):

  • Auto-creates user if needed
  • DirectLogin → token
  • Grants system roles (CanCreateEntitlementAtAnyBank, CanCreateBank)
  • Creates a throwaway bank (ldtst-<random>)
  • Grants bank-level CanCreateAccount
  • If --loop-v7 > 0: grants the system/bank roles v7 endpoints require (CanGetAnyUser, CanGetCacheConfig, CanGetCacheInfo, CanGetCacheNamespaces, CanGetDatabasePoolInfo, CanGetMigrations, CanGetConnectorHealth, CanDeleteEntitlementAtAnyBank, CanGetCustomersAtOneBank, CanGetCardsForBank)

Accounts + views loop (--loop-accounts iterations):

  • Creates an account (v4.0.0)
  • Lists views (v5.0.0), creates a custom view (v3.0.0), lists views again
  • Fetches account details (v6.0.0), balances (v5.1.0)
  • Lists GET /my/accounts (v3.0.0)

Consent loop (--loop-consents iterations):

  • Creates an IMPLICIT consent (v5.1.0)
  • Answers challenge (v3.1.0) with sandbox default "123456"
  • Lists consents, uses the Consent-JWT to call /users/current/user_id
  • Revokes the consent

v7.0.0 sweep (--loop-v7 iterations × --workers-v7 goroutines):

Before the concurrent phase runs, a single v7 entitlement POST + DELETE round-trip is fired using the bootstrap user, to exercise a v7 write path.

Then each worker runs the following in its inner loop:

  • No auth: /root, /banks, /banks/{BANK_ID}, /features, /api/versions, /system/connectors, /resource-docs/v7.0.0/obp
  • User auth: /users/current, /providers, /banks/{BANK_ID}/accounts, /my/banks/{BANK_ID}/accounts/{ACCOUNT_ID}/account, /banks/{BANK_ID}/accounts/{ACCOUNT_ID}/owner/account, /cards
  • Role-gated: /banks/{BANK_ID}/cards, /banks/{BANK_ID}/customers, /users, /users/user-id/{USER_ID}, /system/cache/config, /system/cache/info, /system/cache/namespaces, /system/database/pool, /system/migrations
  • Backend-stall simulation: /system/connectors/stored_procedure_vDec2019/health (dials the stored-procedure connector; stalls until the client's 30s timeout when that connector isn't configured)

The counterparty endpoint is skipped — no counterparty is created in the setup flow.

Concurrency, users, and load shape

./run.sh --users-v7 10 --workers-v7 40 --loop-v7 20
  • --workers-v7 40 → 40 concurrent goroutines in one process.
  • --users-v7 10 → 10 distinct logged-in users; workers pick tokens round-robin (4 workers per user).
  • Each worker runs --loop-v7 sweeps sequentially.

All workers share the bootstrap user's bank and first account. Endpoints that check account ownership (/my/banks/.../accounts/..., /banks/.../accounts/.../owner/account) will 4xx for non-bootstrap users — expected, and visible in the per-endpoint status breakdown.

Alternative: run multiple ./run.sh terminals in parallel. Each spins up its own user/bank/accounts, so they don't collide — but each also pays the full setup cost, and last_run.log gets overwritten by the last one to finish.

Deadlock / pool-starvation detection

Every v7 request logs its duration inline. Any call > 5s is flagged live with [SLOW — suspect stall]. At the end of the v7 sweep a per-endpoint table prints p50 / p95 / p99 / max latency, a status-code breakdown, and counts of slow/timed-out calls.

A verdict line summarises:

  • No stalls: clean — no request exceeded 5s.
  • Slow requests but no hard timeouts: contention present; increase --workers-v7 to find the breaking point.
  • Timeouts observed (cap is the HTTP client's 30s timeout): suspect deadlock or pool starvation. Under the v7 request-scoped-connection design, every v7 request pins one HikariCP connection for the whole handler lifetime; if concurrent workers exceed the pool size, later requests queue on pool acquire for up to the server's acquire timeout. Mitigations: raise the pool size, or lower --workers-v7.

How to tell the failure modes apart:

Observation Likely cause
No slow, no timeouts, all 2xx Clean. Pool sized adequately for the concurrency.
p95 spikes but no timeouts Contention, headroom shrinking — step --workers-v7 up to find the breaking point
Timeouts cluster at ~30s exactly Pool starvation (HikariCP acquire timeout). Raise pool, not concurrency
Timeouts before 30s, or hangs that never return Real deadlock at the DB level
Only one endpoint slows down That endpoint holds the connection too long (slow auth lookup, external dial, etc.)

The stored_procedure_vDec2019/health endpoint is the fastest way to reproduce pool starvation: it dials an external connector that won't answer on a dev instance, so every call holds its connection for 30s until the client times out.

Output

  • All stdout is tee'd to go-app/last_run.log.
  • Each line from a concurrent worker is prefixed [wN] (1-indexed).
  • The final block is the per-endpoint stats table + verdict.

Requirements

  • Go 1.24+ (see go-app/go.mod)
  • A reachable OBP API instance
  • A consumer key valid on that instance
  • The test user must be super-admin or have CanCreateEntitlementAtAnyBank
    • CanCreateBank (granted automatically if the auto-created user has super-admin rights)

Files

  • run.sh — launcher, flag parsing, forwards to go run
  • go-app/main.go — setup + accounts + consents phases, flag definitions
  • go-app/v7_0_0.go — v7.0.0 sweep, user pool, entitlement round-trip
  • go-app/stats.go — per-endpoint latency recording, verdict
  • go-app/last_run.log — full stdout of the last run

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors