El viaje de ida y vuelta
Descomponer la latencia percibida en sus términos reales —propagación, encolado, procesamiento y renderizado— para aislar cuál de ellos ninguna optimización podrá eliminar jamás.
Cuando pulsas algo y el sistema tarda, no tarda una cosa: tardan varias, encadenadas y de naturalezas distintas. La latencia percibida es una suma de términos, y la diferencia decisiva entre ellos no es cuánto valen hoy, sino cuál es su límite inferior. Casi todos admiten ingeniería. Uno solo admite geografía.
- Descomponer la latencia percibida en trabajo de cliente, propagación, transmisión, encolado, procesamiento y renderizado.
- Distinguir los términos que la ingeniería puede reducir de los que impone el medio físico.
- Entender por qué la latencia percibida se mide de la intención al píxel, no de la petición a la respuesta.
- Reconocer la cascada de viajes dependientes como el multiplicador silencioso del camino crítico.
Anatomía de una espera
Un usuario percibe una única magnitud: el tiempo entre el gesto y el cambio en pantalla. Esa magnitud es agregada, y desagregarla es lo que permite razonar. En una arquitectura que consulta al servidor para resolver la interacción, la espera contiene al menos seis contribuciones distintas.
El trabajo de cliente es todo lo que ocurre antes de que el primer byte salga: capturar el evento, validar, serializar, decidir qué pedir. La propagación es el tiempo que la señal tarda en recorrer la distancia, y depende únicamente de cuántos kilómetros hay y de a qué velocidad viaja la luz en el medio. La transmisión es el tiempo de empujar los bits a la línea, y depende del tamaño del mensaje dividido por el ancho de banda. El encolado es la espera en buffers: routers intermedios, colas de conexión, colas de peticiones del servidor, colas de bloqueos de la base de datos. El procesamiento es el trabajo útil del servidor. El renderizado es lo que el cliente hace con la respuesta hasta que hay píxeles nuevos.
// Modelo aditivo de la latencia percibida, por terminos independientes
const percibida =
cliente + // captura del evento y preparacion de la peticion
propagacion + // distancia dividida por la velocidad en el medio
transmision + // tamano del mensaje dividido por el ancho de banda
encolado + // esperas en buffers intermedios y en el servidor
procesamiento + // el trabajo real que resuelve la peticion
render; // layout, pintura y composicion hasta el pixel
flowchart LR A[Gesto del usuario] --> B[Trabajo en el cliente] B --> C[Propagacion de ida] C --> D[Encolado en la red y en el servidor] D --> E[Procesamiento] E --> F[Propagacion de vuelta] F --> G[Render y pintado] G --> H[El usuario ve el cambio] style C fill:#f38ba8,color:#11111b style F fill:#f38ba8,color:#11111b style H fill:#a6e3a1,color:#11111b
Los dos nodos marcados en rojo son los que este nivel va a examinar con lupa. No porque sean siempre los mayores —bajo carga, el encolado los aplasta— sino porque son los únicos que no dependen de ninguna decisión de ingeniería tuya.
Conviene notar que los seis términos no solo tienen magnitudes distintas: dependen de variables distintas y se comportan de maneras distintas. La transmisión depende del tamaño del mensaje; la propagación no depende del tamaño en absoluto, sino de la distancia. El procesamiento depende de la complejidad de la consulta; el encolado no depende de tu consulta sino de las de los demás. Meterlos todos en una sola cifra llamada “latencia” es cómodo para el informe y desastroso para el razonamiento, porque cada uno responde a una palanca diferente y algunos no responden a ninguna.
El término incompresible
Toma cada término y pregúntate: ¿cuál es su valor mínimo si dispusiera de recursos ilimitados?
El trabajo de cliente y el renderizado tienden a un mínimo pequeño con mejor código y mejor hardware. La transmisión tiende a cero conforme crece el ancho de banda, que ha mejorado en órdenes de magnitud a lo largo de décadas. El encolado tiende a cero si sobreprovisionas y evitas la contención. El procesamiento tiende a un mínimo bajo con índices, cachés y máquinas más rápidas. Cinco de los seis términos son, en el límite, problemas de dinero e ingeniería.
La propagación no. Su mínimo es la distancia dividida por la velocidad de la señal en el medio, y ninguna de las dos cantidades está bajo tu control: la primera la fija el mapa y la segunda la fija la física del vidrio. Puedes acortar la distancia moviendo el servidor —eso es lo que hace una red de borde— pero no puedes hacerla desaparecer mientras haya un servidor distinto del dispositivo.
David Patterson formuló esta asimetría en 2004 con un título que se ha vuelto proverbial: latency lags bandwidth. A lo largo de la historia del cómputo, el ancho de banda de discos, memorias y redes ha crecido mucho más deprisa que la reducción de sus latencias. La razón es estructural: el ancho de banda se compra en paralelo —más canales, más fibras, más carriles— mientras que la latencia es un camino serie que hay que recorrer entero. Esperar que el próximo salto tecnológico arregle tu latencia de red es apostar contra la tendencia de cincuenta años.
Casi todas las cifras de propagación se publican como tiempo de ida, y casi todas las interacciones necesitan la vuelta: el usuario no puede continuar hasta que la respuesta llega. Por eso la unidad útil para razonar sobre interfaces es el viaje completo, el doble de la propagación de ida. Y si el protocolo requiere establecer la conexión antes de transportar el primer byte útil —saludo de transporte, negociación criptográfica— ese establecimiento cuesta sus propios viajes completos antes de que la petición real haya salido.
De la intención al píxel
Un error habitual es medir la latencia entre la salida de la petición y la llegada de la respuesta, y llamar a eso “la latencia”. Ese intervalo es el que el panel del servidor sabe medir, pero no es el que el usuario vive.
La ventana relevante empieza antes: cuando el evento de entrada llega al sistema. Y termina después: cuando el píxel ha cambiado en la pantalla. Entre medias hay trabajo que ninguna métrica de red registra —deserializar, reconciliar el árbol de la interfaz, recalcular disposición, pintar, componer— y, al final, un efecto de cuantización que suele olvidarse: la pantalla se refresca a intervalos discretos, así que la respuesta nunca se entrega en el instante en que está lista, sino en el siguiente refresco. A una tasa de refresco de sesenta imágenes por segundo, ese cuanto es del orden de la sesentava parte de un segundo, unos dieciséis milisegundos.
Si vas a fijar un objetivo de latencia, fíjalo de entrada a píxel. Un servidor que responde rapidísimo dentro de una interacción que se siente pastosa es un servidor que está optimizando la métrica equivocada con mucha diligencia. La única medida que un usuario puede refutar es la que él mismo experimenta.
La cascada: cuando los viajes se multiplican
Hasta aquí hemos supuesto un viaje. La patología real aparece cuando los viajes son dependientes: cuando la segunda petición no puede formularse hasta conocer la respuesta de la primera.
// Cascada: cada await encadena un viaje completo mas
const sesion = await pedirSesion(); // viaje 1
const perfil = await pedirPerfil(sesion.userId); // viaje 2
const items = await pedirItems(perfil.listaId); // viaje 3
// Paralelo: los viajes independientes comparten el mismo intervalo
const [a, b] = await Promise.all([pedirA(), pedirB()]);
El coste no se suma, se multiplica por la profundidad de la cadena. Tres dependencias encadenadas contra un servidor lejano no cuestan un viaje: cuestan tres, y ninguna optimización del servidor toca ese factor. De ahí que el trabajo serio de rendimiento distribuido consista, casi siempre, en aplanar cascadas: agrupar peticiones, precargar, mover la composición al servidor, colapsar niveles.
La métrica que conviene vigilar, por tanto, no es cuántas peticiones hace una pantalla sino cuál es la profundidad máxima de dependencias de su camino crítico. Veinte peticiones independientes cuestan un viaje; tres encadenadas cuestan tres. La primera cifra asusta en un gráfico de red y la segunda es la que el usuario nota, y casi ninguna herramienta de observación presenta la segunda.
Existe además una cascada más sutil, la que atraviesa capas: el navegador pide un documento, el documento referencia un guion, el guion pide datos, los datos referencian una imagen. Cada nivel de indirección descubre el siguiente solo después de haber recibido el anterior, y el resultado es una cadena de viajes que nadie escribió como tal en ninguna función. Aplanarla exige declarar por adelantado lo que se va a necesitar, que es la razón de ser de casi todas las técnicas de precarga.
Propagación
Distancia entre velocidad de la señal. Es el único término que la ingeniería no puede reducir sin mover geografía.
Transmisión
Tamaño entre ancho de banda. Mejora con el tiempo y con comprimir, y en cargas pequeñas es casi despreciable.
Encolado
Espera en buffers. Es cero cuando el sistema está ocioso y explosivo cuando se acerca a la saturación.
Renderizado
El tramo final hasta el píxel, cuantizado por el refresco de pantalla. Nunca aparece en el panel del servidor.
El término de encolado engaña porque se comporta bien hasta que deja de hacerlo. En cualquier sistema con llegadas irregulares, el tiempo de espera crece de forma no lineal conforme la utilización se acerca al cien por cien: doblar la carga en un sistema poco ocupado apenas se nota, y añadir un poco de carga a un sistema casi saturado dispara la espera. Por eso las latencias de cola —los percentiles altos— se degradan mucho antes que la mediana, y por eso una arquitectura que solo mira la media no se entera de nada hasta que ya es tarde.
La tentación natural al ver una suma de seis sumandos es tratarlos como fungibles: si uno crece, recorto otro. Es lo que hace la optimización clásica, y funciona hasta que choca con la asimetría fundamental de esta lista. Cinco de los seis términos son contingentes: dependen de decisiones —qué máquina, qué índice, qué formato, cuánta capacidad— y por tanto son negociables con dinero, con tiempo o con ingenio. El sexto, la propagación, es necesario en el sentido estricto: dada una arquitectura que consulta a una máquina remota dentro del camino crítico de una interacción, existe un valor por debajo del cual esa interacción no puede completarse, y ese valor no lo fija tu equipo sino la posición relativa de dos puntos sobre la Tierra. La consecuencia es más profunda de lo que parece. Significa que el rendimiento de una arquitectura cliente-servidor no es un continuo que se pueda mejorar indefinidamente con esfuerzo, sino una función con un suelo, y que ese suelo es una propiedad del diseño, no de la implementación. Ningún perfilador te lo va a señalar, porque un perfilador mide lo que ocurre, no lo que es imposible que no ocurra. Por eso la discusión sobre local-first no empieza en el terreno de la optimización sino en el de la topología: no se trata de recorrer el camino más rápido, sino de preguntarse si ese camino tiene que existir. Todo lo que sigue en este nivel es el desarrollo de esa única pregunta.
- Toma una interacción cotidiana de una aplicación que uses y enumera cuál de los seis términos crees que domina la espera. Justifica tu elección sin medir.
- Explica con tus palabras por qué el ancho de banda ha mejorado más deprisa que la latencia, apoyándote en la idea de paralelo frente a serie.
- Dibuja la cascada de una operación real que conozcas e identifica cuántos viajes dependientes contiene y cuáles podrían paralelizarse.
- Argumenta por qué medir de petición a respuesta subestima sistemáticamente lo que el usuario percibe, y nombra dos tramos que ese intervalo omite.
- Razona qué le pasa al término de encolado cuando un servicio pasa del setenta por ciento al noventa y cinco por ciento de utilización, y por qué la mediana lo oculta.