Titanium : Cœur backend scalable pour messageries personnalisées — Du prototype à la production
Les développeurs qui construisent des messageries personnalisées commencent souvent par XMPP/ejabberd ou Matrix/Synapse. Ces plateformes offrent un transport prêt à l'emploi, la fédération et le chiffrement de bout en bout. Mais l'adaptation à une expérience utilisateur moderne se heurte à des obstacles.
XMPP avec ejabberd sur Erlang est compact et résistant à la charge, mais :
- Les stanzas XML ajoutent une surcharge de parsing.
- Les salles MUC ne scalent pas linéairement : les pics CPU explosent avec le nombre de participants.
- Les extensions partielles XEP entraînent des conditions de course dans la logique métier.
Matrix est puissant, mais son architecture en couches rend les modifications complexes : Synapse sur Python peine avec le GIL pour les tâches temps réel.
Les configurations hybrides — comme ejabberd pour le transport + FastAPI pour l'API/OTP/réactions — provoquent un état scindé, des conversions XML<->JSON et des files d'attente supplémentaires. La source unique de vérité se brouille entre les composants.
Leçon clé : Il est plus simple de construire un backend from scratch pour un contrôle total sur l'état et les performances.
Titanium v1 : FastAPI sous charge
La première version de Titanium sur FastAPI avec WebSocket gérait les fonctionnalités de base : chats, UIN, médias, pts-sync. Le prototype tenait 500 RPS dans un scénario de 1000 utilisateurs avec 120 msg/s, 40 médias/min et 7 % de déconnexions.
Les RPS étaient mesurés sur des chemins complets : envoi -> livraison -> accusé -> sync. Cela couvrait :
- Validation des droits d'accès.
- Mises à jour pts (points de message pour delta-sync).
- Pousses WebSocket temps réel.
- Retentatives sur réseaux instables.
- Journaux de mises à jour pour clients hors ligne.
Des problèmes sont apparus à grande échelle :
- Le GIL limitait la concurrence WebSocket.
- PostgreSQL se bloquait sur haute contention dans les tables pts.
- Le stockage média nécessitait des liens temporaires, pas permanents.
Architecture Titanium : Où vit la vérité
Titanium définit des règles strictes de gestion d'état :
- Serveur comme source de vérité : Ne jamais faire confiance aux clients ; tous les changements via API.
- Mécanisme pts : Delta-sync via points de message, style Telegram.
- Journal de mises à jour : Log immuable pour relecture d'état.
- État distribué : Sharding par UIN, sans sessions sticky.
- Médias : Signatures temporaires style S3, TTL 24h.
Composants clés :
- Gateway : WebSocket + HTTP/2, limitation de débit, JWT.
- Core : Logique métier, pts, ACL (par UIN/groupes).
- Stockage : PostgreSQL (états) + Redis (sessions/files) + S3 (médias).
- Workers : Celery/RQ pour tâches asynchrones (traitement média, bots).
Exemple pts-sync (pseudocode) :
% Le client demande un 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}}.
Passage à Erlang/OTP : Scalabilité et résilience
Python ne gérait pas 10k WebSockets concurrents. Erlang/OTP a résolu ça :
- Modèle actor : Chaque UIN obtient un arbre de superviseurs avec gen_server.
- Rechargement à chaud de la VM sans downtime.
- Mnesia/ETS natifs pour stockage basse latence.
- Cowboy pour scaler WebSocket à 1M+ connexions par nœud.
Benchmarks :
| Scénario | FastAPI (Python) | Titanium (Erlang) |
|----------|-------------------|--------------------|
| 1k users, 120 msg/s | 500 RPS | 5k RPS |
| 10k WS | 20% CPU | 5% CPU |
| Médias 100/min | 2s latence | 200ms |
Exemple session gen_server :
-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.
Chats de groupe et cas limites
Les groupes sont une bête noire. Abandon des MUC pour :
- Salles shardées : Leader par hash(UIN_list), followers répliquent les deltas.
- Présence : ETS pour en ligne/hors ligne, pubsub au reconnexion.
- Réseaux dégradés : Envoi optimiste + ACK serveur, comblement des trous depuis les logs.
Cas gérés :
- Envois dupliqués (idempotence par msg_id).
- Réordonnancement de paquets (timestamp + seq).
- Onglets sleep/wakeup (fallback long-poll).
- Webhooks bots avec retries (dead letter queue).
Leçons clés
- Titanium n'est pas un clone MTProto — c'est une pile temps réel légère : pts, logs, sharding.
- Erlang/OTP offre 10x RPS à 1/4 CPU vs Python.
- La vérité serveur prévient les incohérences client.
- Médias/bots déchargés asynchrones ; core focalisé sur la livraison.
- Scale : 3 nœuds gèrent 50k users, 1k msg/s sans fédération.
— Editorial Team
Aucun commentaire pour le moment.