Server capacity

Updated 2026-05-30 · after the 6.61–6.64 fixes were rolled out.

The main point: pre-production, no legacy

There are no real users on the production server yet. Every installation is a test one. Hence:

Estimates by load band

Audience, MAUConcurrent WS (peak)State of a single VPSMain bottleneck
≤ 30k ≤ 3k 10× headroom, idle Nothing in our stack
30k — 100k 3k — 10k The target band, comfortable Workerman's CPU handles pongs and events easily. The database works on primary-key lookups. 500 MB–1 GB of RAM for the sockets.
100k — 200k 10k — 20k Running at the limit The single Workerman process overheats at peak. Let a bug with dropped WebSockets surface and degradation begins.
> 200k > 20k We hit the ceiling Phase C is needed (Workerman count=4 plus Redis pub/sub for shared $hosts/$guests).

What was fixed in 6.61–6.64

MetricBefore the fixesAfter 6.64Where it was done
ble_token_renew queued while the owner is offline 1300 per 4 h 1 (UNIQUE INDEX dedup) Server — works with any client
HTTP retries on a network failure 5 keys × 1/min = 7200 a day ~50 a day (30-minute backoff) Client 6.64 — a new APK is required
WebSocket reconnects on a flapping network ~10/min (1 s retry) ~1/min (60 s backoff) Client 6.62+ — a new APK is required
Database hello lookups during WebSocket flapping ~50/s across 10k clients ~5/s Server — the 5-second rate limit protects against any client
Failed fire events in a guest's Prefs 60+ a day per guest with an expired token 0 Client 6.63
Recompose waves on guest_ok on every numbers_update only on a real change Client 6.64

What works immediately for every client (server-side fixes)

The server-side protections live in the handling of incoming messages — they apply regardless of the client version.

What only works after the client updates

Decision: raised $MIN = '6.66'. Any old client, on its first versionCheck gets a blocking "Update the app" dialog (the UI already exists, see App.kt:checkVersionAsync).

Scaling Workerman out — when it becomes necessary

Right now count=1 — a single PHP process holds ALL the WebSockets in the arrays $hosts/$guests/$calls in-memory.

The concrete thresholds:

What to do as 15k approaches:

  1. Install Redis on the VPS (apt install redis-server).
  2. Workerman count=4 (matching the VPS core count).
  3. Move $hosts/$guests/$calls into a Redis HASH with a TTL.
  4. Pub/sub between workers: when a message arrives at W1 but the recipient sits on W2, relay it through the channel.

Effect: linear scaling up to 4×, raising the ceiling to roughly 40–60k concurrent.

Moving to a CDN — when it becomes necessary

Today the 22 MB APK is served from our own VPS to every user. At 100 thousand that is 2.2 TB of outbound traffic per release — our own link may not cope, or may prove expensive.

Prepared in 6.64:

When the move becomes necessary:

  1. A Cloudflare R2 bucket → upload entrixy.apk.
  2. Custom domain apk.entrixy.com through the Cloudflare CDN.
  3. In _config.php: $GLOBALS['apk_url'] = 'https://apk.entrixy.com/entrixy.apk';
  4. In the deploy script, after assembleRelease add r2 cp app-release.apk apk-bucket/entrixy.apk.

At the present scale — dozens of test installations — no move is needed.

The action list: what is on the server and in the app now

To get every fix working:
  1. On the server: sudo supervisorctl restart dialer-ws.
  2. Any version below 6.66 gets a mandatory update dialog on its next launch.
  3. Once 6.66 is rolled out, load from reconnect storms and retries drops by an order of magnitude.