Operating

Load Testing

Learn to perform load testing on your Blnk server using k6.

Use load testing to ensure that your Blnk deployment can handle expected traffic and maintain performance under various conditions. This is a critical step in setting up your Blnk server.

Blnk includes a comprehensive load testing suite built with k6, a modern load testing tool. This guide walks you through finding, configuring, and running load tests to validate your Blnk server’s performance across different scenarios.

Before you start

Make sure you have:

  1. Cloned the Blnk repository to your local machine
  2. Docker and Docker Compose installed, for running the Blnk server
  3. k6 installed on your machine. Visit k6.io for installation instructions
  4. Your Blnk server running and accessible

Getting started

You can find the load test files in the tests/loadtest directory of the Blnk repository. Navigate to this directory to access all available test scripts:

cd tests/loadtest

Main test script

The primary load test script (script.js) supports seven configurable load-testing scenarios, each controlled via environment variables. Each scenario targets a specific aspect of your Blnk server’s transaction performance, allowing you to test different load patterns and system behaviors.

Every request is posted with inflight: true and skip_queue: false unless you override INFLIGHT or SKIP_QUEUE.

  • Baseline: Steady, constant load to measure normal performance.
  • Ramp: Gradually increasing load to find when your server struggles.
  • Contention: Tests when one balance gets most of the traffic.
  • Hot source: One balance funding many destinations (A → N).
  • Hot destination: Many balances funding one destination (N → A).
  • Soak: Long-duration test (2 hours) to find memory leaks and stability issues.
  • Spike: Sudden traffic surge to test recovery behavior.

Use the SCENARIO environment variable to select which scenario to run. Each scenario has specific configuration options:

Runs a steady, constant load to measure your server’s normal performance. Use this to see how fast your server responds under normal conditions.

k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=baseline --env VUS=100 --env DURATION=5m script.js
VUSintegerdefault 100query

Number of virtual users to run concurrently

DURATIONstringdefault 5mquery

Test duration (e.g., “5m”, ”30s”, “1h”)

Slowly increases traffic over time to find when your server starts to struggle. Starts with 100 users and gradually increases to 1000 users across three stages.

k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=ramp \
  --env START_VUS=100 \
  --env VUS1=300 --env STAGE1=5m \
  --env VUS2=600 --env STAGE2=5m \
  --env VUS3=1000 --env STAGE3=5m \
  script.js
START_VUSintegerdefault 100query

Initial number of virtual users at the start of the test

VUS1integerdefault 300query

Target number of virtual users for the first ramp stage

STAGE1stringdefault 5mquery

Duration of the first ramp stage

VUS2integerdefault 600query

Target number of virtual users for the second ramp stage

STAGE2stringdefault 5mquery

Duration of the second ramp stage

VUS3integerdefault 1000query

Target number of virtual users for the third ramp stage

STAGE3stringdefault 5mquery

Duration of the third ramp stage

Tests what happens when one balance gets most of the traffic while others get less. This simulates real-world scenarios where some accounts are much busier than others.

k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=contention \
  --env HOT_DEST=@hot-balance-0001 \
  --env HOT_RPS=308 \
  --env RANDOM_RPS=132 \
  --env DURATION=5m \
  --env VUS=200 \
  --env MAX_VUS=800 \
  script.js
HOT_DESTstringdefault @hot-balance-0001query

The balance identifier that receives concentrated traffic (70% of requests)

HOT_RPSintegerdefault 308query

Requests per second targeting the hot balance

RANDOM_RPSintegerdefault 132query

Requests per second targeting random balances

DURATIONstringdefault 5mquery

Test duration (e.g., “5m”, ”30s”, “1h”)

VUSintegerdefault 200query

Number of pre-allocated virtual users

MAX_VUSintegerdefault 800query

Maximum number of virtual users that can be allocated

Tests one balance funding many others (A → N), such as a payout or treasury account. Every transaction draws from the same source and credits a new destination.

k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=hot_source \
  --env HOT_SOURCE=@hot-source-0001 \
  --env RATE=300 \
  --env DURATION=5m \
  --env VUS=200 \
  --env MAX_VUS=800 \
  --env SOURCE_BUCKETS=1 \
  script.js
HOT_SOURCEstringdefault @hot-source-0001query

The balance identifier that funds every transaction

RATEintegerdefault 300query

Requests per second targeting the hot source

DURATIONstringdefault 5mquery

Test duration (e.g., “5m”, ”30s”, “1h”)

VUSintegerdefault 200query

Number of pre-allocated virtual users

MAX_VUSintegerdefault 800query

Maximum number of virtual users that can be allocated

SOURCE_BUCKETSintegerdefault 1query

Number of shards to split the hot source across. Values above 1 spread traffic over @hot-source-0001-bucket-1 through -bucket-N

Tests many balances funding one (N → A), such as a collections or settlement account. Every transaction debits a new source and credits the same destination.

k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=hot_destination \
  --env HOT_DEST=@hot-balance-0001 \
  --env RATE=300 \
  --env DURATION=5m \
  --env VUS=200 \
  --env MAX_VUS=800 \
  --env DESTINATION_BUCKETS=1 \
  script.js
HOT_DESTstringdefault @hot-balance-0001query

The balance identifier that receives every transaction

RATEintegerdefault 300query

Requests per second targeting the hot destination

DURATIONstringdefault 5mquery

Test duration (e.g., “5m”, ”30s”, “1h”)

VUSintegerdefault 200query

Number of pre-allocated virtual users

MAX_VUSintegerdefault 800query

Maximum number of virtual users that can be allocated

DESTINATION_BUCKETSintegerdefault 1query

Number of shards to split the hot destination across. Values above 1 spread traffic over @hot-balance-0001-bucket-1 through -bucket-N

Runs a steady load for a long time (default: 2 hours) to check if your server has memory leaks or other problems that only show up after running for a while.

k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=soak --env VUS=200 --env DURATION=2h script.js
VUSintegerdefault 200query

Number of virtual users to run concurrently

DURATIONstringdefault 2hquery

Test duration (e.g., “5m”, ”30s”, “1h”, “2h”)

Simulates a sudden traffic spike to see if your server can handle the surge and recover afterward.

k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=spike \
  --env BASE_RPS=200 \
  --env SPIKE_RPS=600 \
  --env WARM=2m \
  --env PEAK=1m \
  --env COOL=3m \
  --env PRE_VUS=300 \
  --env MAX_VUS=1200 \
  script.js
BASE_RPSintegerdefault 200query

Baseline requests per second during warm-up and recovery phases

SPIKE_RPSintegerdefault 600query

Peak requests per second during spike phase

WARMstringdefault 2mquery

Duration of warm-up phase before spike

PEAKstringdefault 1mquery

Duration of spike phase

COOLstringdefault 3mquery

Duration of recovery phase after spike

PRE_VUSintegerdefault 300query

Number of pre-allocated virtual users

MAX_VUSintegerdefault 1200query

Maximum number of virtual users that can be allocated during the spike

Understanding test results

k6 provides comprehensive metrics for each test run. Key metrics to monitor include:

  • http_req_duration: Request latency (p50, p95, p99 percentiles)
  • http_req_failed: Error rate (should be < 0.1%)
  • http_reqs: Total requests processed
  • vus: Virtual users active during the test

Performance thresholds

The main test script (script.js) includes built-in performance thresholds that automatically fail tests if exceeded:

  • Error rate: Less than 0.1% (http_req_failed < 0.001)
  • P95 latency: Less than 300ms
  • P99 latency: Less than 600ms

Exporting results

The main test script automatically exports results to summary.json. Run any scenario:

k6 run --env SCENARIO=baseline script.js

Results are automatically saved to summary.json in the current directory.

Analyze results with TPM tool

The test suite includes a utility script to convert k6 JSON output into transactions per minute (TPM) CSV format.

First, export k6 results as JSON:

k6 run --out json=results.json --env SCENARIO=baseline script.js

Then convert to CSV:

python3 tools/k6_json_to_tpm.py results.json tpm_results.csv
input.jsonstringrequiredquery

Path to k6 JSON output file (use --out json=output.json when running k6)

output.csvstringrequiredquery

Path to output CSV file

The generated CSV includes: minute timestamp, requests, transactions per minute (tpm), success ratio, p50/p95/p99 latency, and apdex score.

Troubleshooting

Connection refused errors

If you see connection errors, verify:

  1. Your Blnk server is running and accessible.
  2. The URL matches your server’s address and port.
  3. Firewall rules allow connections to the Blnk server.

High error rates

If error rates exceed thresholds:

  1. Check Blnk server logs for errors.
  2. Verify database connection pool settings.
  3. Review server resource usage (CPU, memory).
  4. Consider reducing virtual users or request rate.