Elegir una base de datos, una cola y una plataforma de contenedores todavía no es un diseño de sistema. Empieza por quién lee los datos, quién los escribe, con qué rapidez tienen que ser visibles y qué debe pasar cuando falla una dependencia.
Una distinción útil es tratar la arquitectura como las restricciones y fronteras de un sistema, y el diseño de sistemas como las decisiones concretas que satisfacen sus requisitos. En la práctica los términos se solapan. Lo que importa es dejar las suposiciones explícitas, no defender un vocabulario concreto.
Define la carga de trabajo antes de elegir herramientas
Piensa en un panel de telemetría a modo de ejemplo. El proyecto de telemetría aporta el contexto de dominio, pero los números y la arquitectura que siguen son un ejemplo didáctico, no la descripción de su despliegue.
Si 100 dispositivos envían cada uno dos mensajes de 200 bytes por segundo, el caudal de datos en bruto es:
100 dispositivos x 2 mensajes/segundo x 200 bytes = 40.000 bytes/segundo
Ese no es el requisito completo de almacenamiento ni de red. La sobrecarga del protocolo, los índices, las réplicas y la retención aumentan el coste. Un panel que pide un día de datos también puede ser mucho más caro de lo que sugiere su reducido número de peticiones HTTP.
Escribe tanto la carga normal como el pico que quieres soportar. Incluye la latencia aceptable, la frescura de los datos, la retención y una política de pérdida. Sin esos límites, “escalable” no significa nada comprobable.
Sistemas con muchas lecturas: cachea lo correcto
Un objeto que se lee mucho puede beneficiarse de una caché, pero la corrección de la caché depende del producto. Una foto de perfil desactualizada y un estado de pago desactualizado tienen consecuencias muy distintas.
Las estampidas de caché ocurren cuando muchas peticiones descubren un valor ausente o caducado y todas lo recalculan a la vez. Entre las protecciones posibles están combinar las peticiones concurrentes sobre la misma clave, servir un valor obsoleto mientras se refresca, y usar leases para controlar el rellenado de la caché.
Añadir variación aleatoria a los tiempos de caducidad ayuda a repartir las expiraciones entre claves distintas. Por sí solo no impide que miles de peticiones recalculen una única clave popular. El artículo Scaling Memcache at Facebook es una buena descripción de estos problemas y del uso de leases.
En el ejemplo de telemetría, un resumen precalculado de la ventana reciente podría servir el panel principal mientras una consulta aparte se encarga de la exploración histórica. Mide la tasa de aciertos de caché, la carga en origen y la obsolescencia tolerada antes de afirmar que has mejorado algo.
Sistemas con muchas escrituras: las colas mueven el cuello de botella
Una cola puede absorber picos y permitir que la ingesta continúe mientras los workers se ponen al día. No crea capacidad de procesamiento infinita. Si la tasa de llegada se mantiene por encima de la de consumo, el backlog crece hasta que la retención, el espacio en disco o la latencia se vuelven inaceptables.
Distingue dos confirmaciones:
- Aceptado: el evento se ha registrado de forma duradera para procesarlo más tarde.
- Completado: la aplicación ha aplicado el cambio de estado previsto.
Un traspaso en memoria no es una aceptación duradera. Define dónde ocurre la persistencia y qué debe reintentar quien llama si se pierde la confirmación. Usa buffers acotados, contrapresión o una política de descarte deliberada, en lugar de dejar que una cola sin límite se convierta en una caída diferida.
En telemetría puede ser aceptable descartar una muestra marcada explícitamente como de baja prioridad. En registros financieros, descartar eventos en silencio no es un compromiso equivalente.
Los reintentos exigen cambios de estado idempotentes
Un worker puede confirmar una actualización en base de datos y caerse antes de confirmar el mensaje. La siguiente entrega no es prueba de que el primer intento no hiciera nada.
Usa una identidad de evento estable, una restricción de unicidad duradera y una transacción que acople la deduplicación con el cambio de negocio. Una comprobación a nivel de aplicación del tipo “¿existe esto?” seguida de un insert es vulnerable a workers concurrentes.
La guía de notificaciones de pago idempotentes recorre la frontera de la transacción y explica por qué los efectos salientes necesitan su propia estrategia de entrega. Es un problema más acotado y accionable que prometer “exactamente una vez” en todos los componentes.
La consistencia es un requisito, no un eslogan
Durante una partición de red, un sistema distribuido no puede garantizar a la vez consistencia linealizable y disponibilidad para todas las peticiones bajo el modelo CAP. Eso no implica que cualquier elección de base de datos sea simplemente “elige dos de tres”, ni que todo retraso de réplica sea una partición.
Pregúntate qué debe observar un usuario después de escribir. Una réplica de lectura puede valer para un gráfico histórico, pero no para confirmar de inmediato un pago recién completado sin una estrategia explícita de lectura tras escritura. La consistencia eventual tampoco tiene ninguna garantía universal de “unos pocos milisegundos”.
El sharding añade otro conjunto de fronteras: consultas entre shards, transacciones, rebalanceo y claves calientes. Mide primero las consultas, los índices, la contención y el volumen de datos. No des por hecho que partir el almacenamiento va a arreglar un patrón de acceso ineficiente.
Haz visible el fallo y medible la recuperación
Los timeouts limitan cuánto espera quien llama. Los reintentos acotados con backoff y jitter evitan repetir presión de forma inmediata. Un circuit breaker puede cortar las llamadas a una dependencia que falla, pero solo si sus umbrales y su comportamiento de respaldo encajan con la operación.
Para el pipeline del ejemplo, algunas señales útiles son la edad del evento al consumirlo, las muestras rechazadas, la profundidad de la cola, el ritmo de procesamiento y la frescura de los datos de extremo a extremo. Los logs y las trazas ayudan a explicar los fallos; las métricas y las alertas te dicen que se ha cruzado un límite operativo.
La recuperación de la infraestructura es un asunto relacionado pero distinto. Un montaje de HA en Proxmox puede reiniciar una máquina virtual sin garantizar que la aplicación haya procesado su backlog ni recuperado sus conexiones.
Una lista de revisión de diseño
- Enuncia la carga de trabajo, la latencia aceptable y la tolerancia a la pérdida de datos.
- Identifica la confirmación duradera y el almacén de datos autoritativo.
- Explica cómo se gestionan los duplicados, las lecturas obsoletas y los fallos parciales.
- Acota el crecimiento de recursos: buffers, reintentos, retención y pools de conexiones.
- Prueba el fallo de una dependencia y mide la recuperación en la frontera de la aplicación.
La mejor arquitectura no es la que tiene más componentes. Es el diseño más pequeño cuyo comportamiento puedes explicar bajo la carga y los fallos que realmente necesitas soportar.
Referencias
- Designing Data-Intensive Applications, Martin Kleppmann.
- Scaling Memcache at Facebook, USENIX NSDI 2013.
- Making retries safe with idempotent APIs, Amazon Builders’ Library.
- Enterprise Integration Patterns, Gregor Hohpe y Bobby Woolf.