El servidor como punto único
Caído, lejano, saturado o simplemente inalcanzable desde un tren: cuatro fallos distintos que producen la misma experiencia, y por qué la disponibilidad es una consecuencia del diseño y no un número que se compra.
La latencia y la disponibilidad parecen problemas separados, pero son el mismo problema medido en dos escalas. Una respuesta que tarda mucho y una respuesta que no llega difieren en grado, no en naturaleza: en ambos casos el usuario está esperando algo que depende de una máquina que no controla. Cuando esa dependencia está en el camino crítico de cada interacción, has convertido una decisión de arquitectura en un punto único de fallo.
- Distinguir los cuatro modos de indisponibilidad y por qué el usuario no los distingue.
- Entender por qué el fallo gris —lento pero vivo— es más difícil de tratar que el fallo limpio.
- Componer la disponibilidad extremo a extremo y localizar el eslabón que nadie instrumenta.
- Clasificar las tres estrategias de degradación según dónde viva la autoridad sobre el dato.
Cuatro maneras de no estar
Cuando decimos que el servidor “no está”, solemos pensar en un servidor caído. Es el caso menos frecuente y el mejor tratado. Hay otros tres, y son más interesantes.
El servidor caído no responde en absoluto. Es un fallo limpio: se detecta pronto, se distingue con claridad y activa los mecanismos previstos. El servidor lejano responde perfectamente, pero desde el otro lado del planeta; cada interacción hereda el suelo geográfico del nivel anterior y la aplicación se siente pastosa sin que ningún panel muestre un solo error. El servidor saturado responde a veces, tarde y de forma irregular; es el peor de los cuatro. Y el cuarto no es un fallo del servidor en absoluto: eres tú quien no está, en un tren que atraviesa un túnel, en un avión, en un sótano, en un país con itinerancia cara o en una red que bloquea lo que necesitas.
flowchart LR A[Servidor caido] --> Z[La aplicacion no responde] B[Servidor lejano] --> Z C[Servidor saturado] --> Z D[Usuario sin conexion] --> Z Z --> W[Para la persona son el mismo suceso] style Z fill:#f38ba8,color:#11111b style W fill:#f9e2af,color:#11111b
Los cuatro tienen causas distintas, dueños distintos y remedios distintos. Para la persona sentada delante son indistinguibles: la aplicación no hace lo que le pide. Esa indistinguibilidad es el dato central del capítulo, porque significa que mejorar tres de los cuatro deja el problema intacto.
Un servidor que responde despacio y a veces es más difícil de manejar que uno apagado. Con un tiempo de espera agotado no puedes distinguir tres situaciones muy distintas: que la petición no llegó, que llegó y se está procesando, o que se procesó y la respuesta se perdió. Reintentar es seguro en el primer caso y potencialmente destructivo en el tercero. De ahí que todo sistema serio acabe necesitando operaciones idempotentes y claves de deduplicación: no por elegancia, sino porque la ambigüedad del fallo gris no se puede resolver observando, solo se puede tolerar por diseño.
La consecuencia práctica de esa ambigüedad es que la entrega exactamente una vez no es alcanzable sobre una red no fiable: solo se puede elegir entre entregar como mucho una vez, arriesgando perder la operación, o al menos una vez, arriesgando repetirla. La salida no consiste en resolver la ambigüedad, que es irresoluble, sino en volverla inofensiva haciendo que repetir no cause daño.
// La clave no evita el reenvio: hace que el reenvio sea inocuo
const operacion = {
clave: identificadorUnicoDeIntento, // estable entre reintentos
tipo: 'transferir',
datos,
};
// El receptor recuerda las claves ya aplicadas y descarta duplicados
La disponibilidad se compone, y se compone mal
Es tentador leer la disponibilidad como una cifra que el proveedor publica. Esa cifra describe un centro de datos; lo que el usuario experimenta es otra cosa.
La disponibilidad extremo a extremo de una interacción es, aproximadamente, el producto de la disponibilidad de todos los elementos que están en serie en su camino crítico: su dispositivo, su enlace, la red intermedia, el balanceador, el servicio, la base de datos y cualquier dependencia que el servicio consulte antes de responder. Multiplicar números menores que uno solo puede dar un número menor. Cada dependencia añadida al camino crítico reduce la disponibilidad, nunca la aumenta, salvo que se añada en paralelo con conmutación real.
// En serie, la disponibilidad se multiplica: cada eslabon resta
const enSerie = eslabones.reduce((acc, e) => acc * e.disponibilidad, 1);
// El eslabon que casi nadie instrumenta es el primero
const enlaceDelUsuario = 'tren, tunel, avion, itinerancia, wifi de hotel';
Y aquí está la asimetría incómoda: el eslabón menos fiable de la cadena suele ser el primero, el enlace del propio usuario, y es justamente el único que ni tú ni tu proveedor podéis mejorar. Todo el esfuerzo de redundancia se concentra donde ya es muy fiable, y el eslabón débil queda fuera del alcance de la ingeniería y también fuera del alcance de la medición, porque cuando ese eslabón falla no llega ningún dato al panel que registraría el fallo.
La redundancia funciona, pero solo en la posición correcta. Poner dos elementos en paralelo con conmutación automática eleva la disponibilidad de ese punto, porque hace falta que fallen los dos a la vez. Poner dos elementos en serie la baja, porque basta que falle uno. Buena parte de las arquitecturas que se describen a sí mismas como redundantes tienen, en realidad, mucha redundancia en el interior y una larga cadena en serie desde el usuario hasta ese interior; y la disponibilidad extremo a extremo la fija la cadena, no el interior.
Un usuario que no puede conectarse no produce peticiones fallidas: produce ausencia de peticiones. La telemetría del servidor, por construcción, solo puede contar lo que le llega. Esto convierte la indisponibilidad del cliente en un fallo estructuralmente invisible: cuanto peor es, menos señales genera. Cualquier panel que se lea sin tener esto presente sobrestima sistemáticamente la salud del sistema.
Las tres degradaciones
Ante la pérdida de conexión, una aplicación puede estar diseñada de tres maneras, y la diferencia entre ellas no es de esfuerzo sino de dónde vive la autoridad sobre el dato.
Fallar cerrado
Sin red no hay nada. La interfaz muestra un error o una página vacía. La autoridad vive entera en el servidor y el cliente es un terminal.
Fallar en solo lectura
Una caché permite seguir leyendo lo último conocido, pero escribir está prohibido. La autoridad sigue siendo remota, con una copia de consulta.
Fallar en local
Lecturas y escrituras funcionan sobre la réplica del dispositivo y se propagan cuando haya red. La autoridad es compartida y hay que reconciliar.
El precio
Cada escalón compra disponibilidad pagando con complejidad de consistencia. No hay escalón gratis y no hay escalón universalmente correcto.
El salto interesante es el segundo, del solo lectura al local completo. Leer de una caché es fácil porque no crea información nueva: como mucho muestras algo viejo. Aceptar una escritura sin coordinación crea información que nadie más conoce todavía, y por tanto abre la posibilidad de que dos dispositivos creen información incompatible. Toda la maquinaria de relojes lógicos, detección de conflictos y convergencia que ocupa los niveles siguientes de este recorrido existe para pagar exactamente esa factura.
Cuando la red se parte, un sistema solo tiene dos conductas posibles ante una escritura: rechazarla para no arriesgar incoherencias, o aceptarla asumiendo que habrá que reconciliar. No existe una tercera. Local-first no niega ese dilema: lo resuelve siempre en la misma dirección —aceptar— para una clase acotada de datos, y a cambio invierte en la maquinaria que garantiza que las réplicas volverán a coincidir. La elección no es entre tener el problema o no tenerlo, sino entre pagarlo por adelantado con diseño o pagarlo a plazos con incidencias.
La red como mejora, no como requisito
Las tres degradaciones se pueden resumir en una sola pregunta de diseño: ¿la conexión es un requisito de la interacción o una mejora de la interacción?
Si es un requisito, la disponibilidad de la aplicación está acotada por arriba por la disponibilidad del enlace más débil, que no controlas. Si es una mejora —la interacción se completa localmente y la red sirve para propagar, respaldar y compartir— entonces la disponibilidad está acotada por si el dispositivo enciende, que es un umbral radicalmente distinto.
// Requisito: sin red no hay lectura, y el usuario espera
async function tareasComoRequisito() {
return await pedirAlServidor('/tareas');
}
// Mejora: la lectura es local y la red solo mantiene fresca la replica
function tareasComoMejora() {
sincronizarEnSegundoPlano(); // si hay red; si no, mas tarde
return leerDeLaReplicaLocal(); // devuelve ya, haya red o no
}
Las dos funciones resuelven la misma necesidad de producto y describen aplicaciones incomparables. La segunda no es simplemente más rápida: su tiempo de respuesta y su disponibilidad han dejado de depender de una variable externa.
Este reencuadre tiene una virtud práctica que va más allá de la resiliencia. Cuando la red es un requisito, cada nueva funcionalidad hereda automáticamente la fragilidad del enlace y el equipo acaba escribiendo, una y otra vez, el mismo tratamiento de errores de conectividad en sitios distintos. Cuando la red es una mejora, ese tratamiento se concentra en un solo lugar —el motor que propaga— y el resto del producto se escribe contra un almacén local que siempre responde. La diferencia de complejidad acumulada, a lo largo de años, es considerable, y va en la dirección contraria a la que la intuición sugiere.
Conviene deshacer una confusión frecuente: aceptar escrituras localmente no implica renunciar a la colaboración, a la copia de seguridad ni a la autoridad del servidor sobre lo que le corresponde. Implica solo que esas funciones dejan de estar en el camino crítico de la interacción y pasan a ocurrir después. El servidor sigue existiendo y sigue siendo útil; lo que cambia es que ya no es el cuello por el que tiene que pasar cada gesto del usuario para poder completarse.
Existe una confusión de categorías tan extendida que casi nadie la ve, y consiste en tratar la disponibilidad como una propiedad operacional —algo que se consigue con redundancia, con más regiones, con mejores guardias y con un contrato de servicio más caro— cuando en realidad es una propiedad estructural, fijada el día que se decidió dónde vive la autoridad sobre cada dato. Toda la inversión operacional del mundo se aplica a los tramos que ya son los más fiables de la cadena, y ninguna de ellas toca el tramo que decide el resultado: si el usuario necesita alcanzar una máquina remota para completar una acción, entonces esa acción no puede ser más disponible que la conectividad de ese usuario, y punto. Se puede añadir una región, y sigue siendo cierto. Se puede añadir un continente entero, y sigue siendo cierto. La única transformación que cambia la respuesta es mover la autoridad, y mover la autoridad no es una decisión de infraestructura sino de modelo de datos. De ahí la afirmación que ordena todo este nivel y da sentido al resto del recorrido: la disponibilidad no se compra al proveedor, se decide en el diseño; el proveedor solo puede venderte la disponibilidad del tramo que tú ya habías declarado imprescindible. Y por eso una aplicación que funciona en un tren no es una aplicación con mejor infraestructura, sino una aplicación que hizo una pregunta distinta al principio.
- Enumera las cinco interacciones más frecuentes de una aplicación que uses y clasifícalas según requieran la red o solo la mejoren.
- Simula el fallo gris: describe qué debería ocurrir si una escritura agota su tiempo de espera y no sabes si llegó. Propón el mecanismo que hace seguro el reintento.
- Dibuja la cadena en serie del camino crítico de una de esas interacciones y señala qué eslabón es el menos fiable y quién lo controla.
- Sitúa la aplicación en una de las tres degradaciones y describe qué haría falta exactamente para subirla un escalón.
- Argumenta por qué la telemetría del servidor sobrestima la salud del sistema, y propón una señal que sí capturaría a los usuarios desconectados.