Версия документа 2026-05-29 · Цель: нынешний единственный VPS должен держать до 100 тысяч MAU не падая, а приложение — переехать на CDN бесшовно, когда придёт время.
| Metric | Значение | Source |
|---|---|---|
| MAU | 100,000 | цель |
| DAU | 30,000 | 30% от MAU — норма для утилитарного приложения |
| Одновременных WS | 10 000 (пик 15 тысяч) | ~10% от MAU днём, 15% на пике |
| Действий в сутки | 90,000 | 3 открытия или звонка на дневного пользователя |
| Запросов HTTP API в секунду (в среднем) | 3-5 | в основном key_create, host_sync и crash_report |
| Запросов HTTP API в секунду (на пике) | 30-50 | после массового выпуска, обновляющего номера |
count=1 — один процесс PHP держит ВСЕ соединения WebSocket в массивах $hosts, $guests, $calls in-memory.File: HostService.kt:1424-1432. Сегодня syncPendingHostKeys() вызывается каждые 60 с и бьёт в Api.keyCreate по каждому ожидающему ключу без всякого отката.
Хранить в Prefs per-localId: attempt_at_id и attempt_count_id. Откат: 30 с → 60 с → 2 мин → 5 мин → 15 мин → 30 мин (потолок). При успехе сбрасывается.
File: Vault.kt:213. Логика та же.
File: GuestConn.kt (5+ мест). Сравнить новое parseNumbers с предыдущим, по хешу или напрямую. Совпали — не инкрементировать AppState.overridesVersion. Это убирает волны перерисовки — 80 чтений SharedPreferences × 20 карточек — при каждой инициализации WebSocket.
In server.php case 'host_hello' / 'guest_hello': если тот же device_id / user_key присылал hello меньше N секунд назад, закрыть соединение, не трогая базу. N = 5 с.
Защита на случай, если клиент со сломанной логикой шлёт сотню hello в секунду.
In enqueueHostMsg: если у этого host_id уже больше 200 строк, удалять самую старую через DELETE FROM pending_host_msgs WHERE host_id=? ORDER BY id ASC LIMIT 1. Защита от бесконечного роста очереди при злонамеренной активности.
In _config.php:
$GLOBALS['apk_url'] = 'https://entrixy.com/entrixy.apk';
$GLOBALS['download_page_url'] = 'https://entrixy.com/download';
In api/version_check.php: 'download_url' => $GLOBALS['apk_url'].
Когда состоится переезд на CDN, изменится ровно одна строка в _config.php to https://apk.entrixy.com/entrixy.apk (Cloudflare R2 со своим доменом). Выпуск приложения ни при чём.
Клиент уже проверяет подпись APK при загрузке (debug-хранилище, см. build.gradle.kts:23-29). Для CDN этого достаточно: даже если узел CDN подменит файл, подпись не сойдётся и Android откажется его ставить. На клиенте ничего доделывать не нужно.
count=1 Workerman без труда держит порядка 10 тысяч одновременных WebSocket — server.php стартует с ним уже на строке 40. Первым узким местом станет процессорное время PHP на пике обмена: 10 тысяч понгов в секунду плюс действия.
Today $hosts, $guests, $calls — массивы в памяти одного процесса. Чтобы запустить несколько процессов, их надо перенести в Redis:
Сообщение пришло на воркер W1, а получатель на W2 — передаём через Redis pub/sub.
Effect: линейное масштабирование до числа ядер VPS. На четырёх ядрах это примерно 40 тысяч одновременно.
Менее гибко и не требует Redis. Каждый пользователь всегда попадает на один и тот же воркер. Но ломается, когда отправитель и получатель оказываются на разных воркерах.
entrixy.apk.apk.entrixy.com → подключить его через CDN Cloudflare._config.php change $apk_url на новый.assembleRelease add r2 cp app-release.apk apk-bucket/entrixy.apk.