# Titanium: Núcleo escalable de backend para mensajeros personalizados — De prototipo a producción
Los desarrolladores que crean mensajeros personalizados suelen empezar con XMPP/ejabberd o Matrix/Synapse. Estas plataformas ofrecen transporte listo, federación y cifrado E2E. Pero personalizarlas para una UX moderna choca con obstáculos.
XMPP con ejabberd en Erlang es compacto y resistente a cargas, pero:
- Los stanzas XML añaden sobrecarga de parsing.
- Las salas MUC no escalan linealmente: el CPU se dispara con el crecimiento de participantes.
- Extensiones parciales de XEP provocan condiciones de carrera en la lógica de negocio.
Matrix es potente, pero su arquitectura en capas complica los cambios: Synapse en Python sufre con el GIL en tareas en tiempo real.
Configuraciones híbridas —como ejabberd para transporte + FastAPI para API/OTP/reacciones— causan estados desincronizados, conversiones XML<->JSON y colas extra. La única fuente de verdad se difumina entre componentes.
Conclusión: Es más simple construir un backend desde cero para tener control total sobre el estado y el rendimiento.
Titanium v1: FastAPI bajo carga
La primera versión de Titanium en FastAPI con WebSocket manejaba funciones básicas: chats, UIN, multimedia, pts-sync. El prototipo aguantó 500 RPS en un escenario de 1000 usuarios con 120 msg/s, 40 multimedia/min y 7% de desconexiones.
RPS se midió en rutas completas: envío -> entrega -> ack -> sync. Esto cubría:
- Validación de derechos de acceso.
- Actualizaciones de pts (puntos de mensaje para delta-sync).
- Envíos en tiempo real por WebSocket.
- Reintentos en redes inestables.
- Logs de actualizaciones para clientes offline.
Problemas a escala:
- GIL limitaba la concurrencia de WebSocket.
- PostgreSQL se bloqueaba con alta contención en tablas pts.
- Almacenamiento de multimedia necesitaba enlaces temporales, no permanentes.
Arquitectura de Titanium: Dónde vive la verdad
Titanium define reglas estrictas de gestión de estado:
- Servidor como fuente de verdad: Nunca confiar en clientes; todos los cambios vía API.
- Mecanismo pts: Delta-sync por puntos de mensaje, al estilo Telegram.
- Log de actualizaciones: Log inmutable para replay de estado.
- Estado distribuido: Sharding por UIN, sin sesiones sticky.
- Multimedia: Firmas temporales estilo S3, TTL de 24h.
Componentes clave:
- Gateway: WebSocket + HTTP/2, limitación de tasa, JWT.
- Core: Lógica de negocio, pts, ACL (por UIN/grupos).
- Almacenamiento: PostgreSQL (estados) + Redis (sesiones/colas) + S3 (multimedia).
- Workers: Celery/RQ para tareas asíncronas (procesamiento multimedia, bots).
Ejemplo pts-sync (pseudocódigo):
% Cliente pide 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}}.
Cambio a Erlang/OTP: Escalabilidad y resiliencia
Python no manejaba 10k WebSockets concurrentes. Erlang/OTP lo solucionó:
- Modelo actor: Cada UIN tiene un árbol de supervisores con gen_server.
- Recarga en caliente de VM sin downtime.
- Mnesia/ETS integrado para almacenamiento de baja latencia.
- Cowboy para escalar WebSocket a 1M+ conexiones por nodo.
Benchmarks:
| Escenario | FastAPI (Python) | Titanium (Erlang) |
|----------|-------------------|--------------------|
| 1k usuarios, 120 msg/s | 500 RPS | 5k RPS |
| 10k WS | 20% CPU | 5% CPU |
| Multimedia 100/min | 2s latencia | 200ms |
Ejemplo gen_server de sesión:
-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 grupales y casos edge
Los grupos son un desafío. Abandonando MUC por:
- Salas shardadas: Líder por hash(UIN_list), seguidores replican deltas.
- Presencia: ETS para online/offline, pubsub al reconectar.
- Redes malas: Envío optimista + ACK servidor, relleno de gaps desde logs.
Casos manejados:
- Envíos duplicados (idempotencia por msg_id).
- Reordenación de paquetes (timestamp + seq).
- Pestañas sleep/wakeup (fallback long-poll).
- Webhooks de bots con reintentos (cola dead letter).
Conclusiones clave
- Titanium no es un clon de MTProto: es un stack ligero en tiempo real: pts, logs, sharding.
- Erlang/OTP da 10x RPS con 1/4 CPU vs Python.
- Verdad en servidor evita inconsistencias cliente.
- Multimedia/bots offload asíncrono; core enfocado en entrega.
- Escala: 3 nodos manejan 50k usuarios, 1k msg/s sin federación.
— Editorial Team
Aún no hay comentarios.