План: 100K юзеров на одном VPS + CDN-ready

Версия документа: 2026-05-29 · Цель: текущий single-VPS должен держать до 100k MAU без падений, плюс приложение должно бесшовно переехать на CDN когда понадобится.

Целевые цифры

МетрикаЗначениеОткуда
MAU100,000цель
DAU30,00030% от MAU (нормально для утилитарного приложения)
Concurrent WS10,000 (пик 15k)~10% MAU днём, 15% в час-пик
Действий/день90,0003 открытия/звонка на DAU
HTTP API req/сек (среднее)3-5в основном key_create, host_sync, crash_report
HTTP API req/сек (пик)30-50после массового релиза с обновлением номеров

Архитектура сейчас

Фаза A — Android клиент СЕЙЧАС, v6.66

Цель: уменьшить генерацию избыточных запросов с клиентов → меньше работы серверу.

A1. HTTP retry exponential backoff на pending host keys

Файл: HostService.kt:1424-1432. Сейчас syncPendingHostKeys() зовётся каждые 60с и дёргает Api.keyCreate для каждого pending ключа без backoff.

Хранить в Prefs per-localId: attempt_at_id и attempt_count_id. Backoff: 30с → 60с → 2м → 5м → 15м → 30м (cap). Сброс при success.

A2. HTTP retry backoff на pending guest bundles

Файл: Vault.kt:213. Аналогичная логика.

A3. overridesVersion compare-before-bump

Файл: GuestConn.kt (5+ мест). Сравнить новый parseNumbers с предыдущим (по hash или прямое сравнение). Если идентично — не бампать AppState.overridesVersion. Снижает recompose-волны (80 SP-чтений × 20 карточек) с каждой WS-инициализации.

Фаза B — Сервер защита и CDN-ready СЕЙЧАС

Цель: защитить сервер от шторма даже если клиент криво ведёт себя, плюс развязать APK-URL от домена.

B1. Rate-limit на host_hello / guest_hello

В server.php case 'host_hello' / 'guest_hello': если тот же device_id / user_key дёргал hello менее N секунд назад — закрыть соединение без DB-запросов. N=5с.

Защита от того, что клиент с битой логикой шлёт hello 100/сек.

B2. Cap pending_host_msgs per host_id

В enqueueHostMsg: если для данного host_id уже > 200 строк — дропнуть старейшую через DELETE FROM pending_host_msgs WHERE host_id=? ORDER BY id ASC LIMIT 1. Защита от бесконечного роста при злонамеренной активности.

B3. CDN-ready: вынести APK URL в config

В _config.php:

$GLOBALS['apk_url'] = 'https://entrixy.com/entrixy.apk';
$GLOBALS['download_page_url'] = 'https://entrixy.com/download';

В api/version_check.php: 'download_url' => $GLOBALS['apk_url'].

В момент миграции на CDN — меняешь ровно одну строку в _config.php на https://apk.entrixy.com/entrixy.apk (Cloudflare R2 + custom domain). Никаких релизов приложения.

B4. Версия APK на CDN — проверка целостности

Клиент при загрузке APK уже верифицирует подпись APK (debug keystore, см. build.gradle.kts:23-29). Этого достаточно для CDN — даже если CDN-узел подменит файл, подпись не совпадёт и Android откажет в установке. Доп.работа на клиенте не нужна.

Фаза C — Workerman scale-out КОГДА УПРЁМСЯ

Текущий count=1 Workerman держит ~10k concurrent WS без проблем (proof: server.php line 40 уже стартует с этим). Узким горлом сначала станет PHP CPU при пиковом обмене сообщений (10k pong/сек + действия).

C1. Workerman count=2-4 с Redis pub/sub для shared state

Сейчас $hosts, $guests, $calls — in-memory массивы в одном процессе. Чтобы запустить несколько процессов, надо вынести в Redis:

Когда message приходит на воркер W1, а получатель сидит на W2 — пересылаем через Redis pub/sub.

Эффект: линейный scale-out до количества ядер VPS. На 4-ядерном = до ~40k concurrent.

C2. Альтернатива: одна машина, NGINX/HAProxy перед Workerman, шардинг по client_ip

Менее гибкий вариант, не требует Redis. Каждый юзер всегда попадает в один и тот же воркер. Но не работает если получатель и отправитель оказались на разных воркерах.

Фаза D — Когда переезжать на CDN ПОЗЖЕ

  1. Cloudflare R2 bucket → залить entrixy.apk.
  2. Custom domain apk.entrixy.com → подключить через Cloudflare CDN.
  3. В _config.php поменять $apk_url на новый.
  4. В deploy-скрипте после assembleRelease добавить r2 cp app-release.apk apk-bucket/entrixy.apk.

Что НЕ делать сейчас

Что я делаю прямо сейчас

  1. Фаза A целиком → ship v6.66.
  2. Фаза B целиком (без рестарта WS — для теста до подтверждения).
  3. Фаза C — пилю отдельной веткой, требует Redis на VPS, отдельная задача.
  4. Фаза D — документация выше, исполняется когда понадобится.