Benchmarks

View Source

Performance measurements for Asobi on a single node. All tests run client and server on the same machine (8 cores, shared schedulers), so real-world deployments with separate client machines will see higher server throughput.

Test environment

  • CPU: 8 cores
  • OTP: 28
  • PostgreSQL: 17 (Docker, max_connections=500, shared_buffers=256MB)
  • DB pool: 200 connections
  • Single Erlang node, no clustering

WebSocket throughput

Heartbeat round-trip: client sends session.heartbeat, server replies with timestamp. Measures the full WebSocket pipeline including JSON encode/decode.

ConnectionsMessagesThroughputRTT p50RTT p99Memory/conn
10010,00035,000 msg/sec1.4ms5.1ms~20KB
3,5007,000,00083,000 msg/sec4.4ms6.5ms~15KB
7,000695,80039,000 msg/sec5.8ms19.9ms~13KB

Peak sustained: ~83,000 messages/sec with 3,500 concurrent connections.

At 7,000 connections the per-message throughput drops because the benchmark client competes with the server for CPU on the same machine.

Blast mode

Fire-and-forget: all messages sent before waiting for replies. Measures raw server processing capacity.

ConnectionsMessages eachTotal deliveredThroughput
3,5002,0007,044,00083,000 msg/sec

All messages delivered with zero loss.

HTTP REST API

100 concurrent players, each running the full lifecycle: register, login, then API reads.

Endpointp50p95p99
POST /auth/register1,463ms1,464ms1,464ms
POST /auth/login724ms1,278ms1,308ms
GET /matches8ms45ms64ms
GET /friends7ms99ms133ms
GET /wallets11ms272ms280ms
GET /players/:id14ms191ms194ms

Registration and login are slow by design: pbkdf2 with 100,000 iterations is CPU-intensive but correct for password security. API reads are sub-15ms p50.

Game type suitability

Mobile / casual (turn-based, party, puzzle)

Excellent fit. Sub-10ms WebSocket RTT, 3,000+ CCU per node. Most mobile games need <100 messages/sec per player, so a single node handles thousands of concurrent players comfortably.

MMO (persistent world)

Viable for zone servers. 3,000-7,000 concurrent connections per node with good latency. A 20,000 CCU MMO would need 5-10 nodes. Erlang's pg-based clustering is designed for this.

Competitive real-time (FPS, fighting, racing)

Not the target. WebSocket (TCP) has a 5-25ms RTT floor. These genres need UDP transport with <3ms latency. Consider Photon or a custom UDP relay alongside Asobi for the game state, using Asobi for everything else (auth, matchmaking, economy, social, leaderboards).

Bottlenecks and tuning

Authentication under load

pbkdf2 saturates CPU during login storms (1,000+ simultaneous registrations). Mitigations:

  • Reverse proxy rate limiting on /auth/* endpoints
  • Auth result caching for repeated token validations
  • Multiple nodes behind a load balancer to spread pbkdf2 work

Database pool

The default pool size matters. With 10 connections, 100+ concurrent DB operations queue up. Recommended:

Deploymentpool_sizePG max_connections
Development50100
Production (single node)200500
Production (cluster)100 per node500-1000

Memory

WebSocket connections use ~13-20KB each. A node with 8GB RAM can sustain ~100,000 connections from memory alone. The practical limit is CPU (message processing) not memory.

Running benchmarks

# HTTP load test (default 100 players)
ASOBI_LOAD_N=500 rebar3 ct --suite=asobi_load_bench

# WebSocket benchmark
# Phase 1: Register players (cached after first run)
# Phase 2: Connect and blast heartbeats
ASOBI_BENCH_PLAYERS=5000 \
ASOBI_WS_N=5000 \
ASOBI_WS_MSGS=2000 \
ASOBI_WS_WAVE=200 \
rebar3 ct --suite=asobi_ws_bench

Environment variables:

VariableDefaultDescription
ASOBI_LOAD_N100HTTP benchmark: concurrent players
ASOBI_BENCH_PLAYERS1000WS benchmark: players to register
ASOBI_BENCH_BATCH50WS benchmark: registration batch size
ASOBI_WS_N500WS benchmark: concurrent connections
ASOBI_WS_MSGS200WS benchmark: messages per connection
ASOBI_WS_WAVE200WS benchmark: connections per wave