Titanium: serverové jádro pro vlastní messenger – od prototypu k škálovatelnosti
Vývojáři, kteří staví vlastní messengery, často začínají s XMPP/ejabberd nebo Matrix/Synapse. Tyto platformy nabízejí připravený transport, federaci a E2E šifrování. Při úpravě pro moderní UX však narazí na limity.
XMPP s ejabberd na Erlangu je kompaktní a odolný vůči zátěži, ale:
- XML stanzky přidávají režii při parsování.
- MUC místnosti se nelineárně škálují: s rostoucím počtem účastníků roste zátěž CPU.
- XEP rozšíření jsou částečně implementována, což způsobuje race conditions v obchodní logice.
Matrix je silný, ale vrstvená architektura ztěžuje změny: Synapse na Pythonu trpí GIL při real-time úlohách.
Hybridní přístup – ejabberd pro transport + FastAPI pro API/OTP/reakce – vedl k split-brain v stavu, převodům XML<->JSON a dalším frontám. Zdroj pravdy se rozptýlil mezi komponenty.
Závěr: Jednodušší je postavit backend od nuly pro plnou kontrolu nad stavem a výkonem.
Titanium v1: FastAPI pod zátěží
První verze Titanium na FastAPI s WebSocket pokryla základní funkce: chaty, UIN, média, pts-synchronizace. Prototyp zvládl 500 RPS ve scénáři s 1000 uživateli, 120 zpráv/s, 40 médií/min, 7 % odpojení.
RPS měřili ne podle /health, ale po celé cestě: odeslání -> doručení -> potvrzení -> synchronizace. To zahrnovalo:
- Validaci oprávnění.
- Aktualizaci pts (message points pro delta-synchronizaci).
- Real-time push přes WebSocket.
- Retry na špatné síti.
- Žurnál aktualizací pro offline klienty.
Problémy se projevily při růstu:
- GIL omezoval souběžnost v WebSocket.
- Databáze (PostgreSQL) se blokovala při vysoké konkurenci v pts-tabule.
- Úložiště médií potřebovalo dočasné odkazy, ne věčné.
Architektura Titanium: kde žije pravda
Titanium definuje pravidla správy stavu:
- Server – zdroj pravdy: klientům nedůvěřujeme, všechny změny jdou přes API.
- Pts mechanismus: delta-synchronizace podle message points, jako v Telegramu.
- Žurnál aktualizací: neměnný log pro replay stavů.
- Distribuovaný stav: sharding podle UIN, bez sticky sessions.
- Média: dočasné podpisy S3-like úložiště, TTL 24 h.
Klíčové komponenty:
- Gateway: WebSocket + HTTP/2, rate limiting, JWT.
- Core: obchodní logika, pts, oprávnění (ACL podle UIN/skupin).
- Storage: PostgreSQL (stavy) + Redis (sessions/fronty) + S3 (média).
- Workers: Celery/RQ pro async (zpracování médií, boty).
Příklad pts-synchronizace (pseudokód):
% Klient žádá delta
handle_get_updates(ReqPts) ->
Updates = db:fetch_updates_after(ReqPts, UserId),
NewPts = lists:max([U#update.pts || U <- Updates]),
{reply, #{updates => Updates, pts => NewPts}}.
Přechod na Erlang/OTP: škálovatelnost a odolnost
Python nezvládl 10k souběžných WebSocket. Přechod na Erlang/OTP problémy vyřešil:
- Actor model: každý UIN – supervisor tree s gen_server.
- VM hot-reload bez výpadků.
- Vestavěné Mnesia/ETS pro low-latency úložiště.
- Cowboy pro WebSocket s 1M+ spojeními na uzel.
Benchmarky:
| Scénář | FastAPI (Python) | Titanium (Erlang) |
|----------|-------------------|--------------------|
| 1k uživatelů, 120 zpráv/s | 500 RPS | 5k RPS |
| 10k WS | 20 % CPU | 5 % CPU |
| Média 100/min | 2s latence | 200ms |
Implementace gen_server pro session:
-module(session).
-export([start_link/1, handle_message/2]).
handle_call({send_message, Msg}, _From, State) ->
case validate_rights(Msg) of
ok ->
NewPts = State#state.pts + 1,
save_update(Msg#update{pts=NewPts}),
broadcast_to_peers(Msg),
{reply, ok, State#state{pts=NewPts}};
{error, Reason} -> {reply, {error, Reason}, State}
end.
Skupinové chaty a hraniční případy
Skupiny jsou samostatnou výzvou. Místo MUC:
- Sharded rooms: leader podle hash(UIN_list), followers replikují delta.
- Presence: ETS pro online/offline, pubsub při reconnect.
- Špatná síť: optimistické odeslání + server ACK, vyplnění mezer z žurnálu.
Seznam zpracování:
- Duplicitní odeslání (idempotence podle msg_id).
- Přeuspořádání paketů (timestamp + seq).
- Spánek/probudení karet (long-poll fallback).
- Bot webhooks s retry (dead letter queue).
Co je důležité
- Titanium není klon MTProto, ale minimální sada pro real-time: pts, žurnál, sharding.
- Erlang/OTP zajišťuje 10x růst RPS při 1/4 CPU oproti Pythonu.
- Zdroj pravdy na serveru zabraňuje nesrovnalostem na straně klienta.
- Média a boty async přes workery, core se soustředí na doručení.
- Škálování: 3 uzly zvládnou 50k uživatelů, 1k zpráv/s bez federace.
— Editorial Team
Zatím žádné komentáře.