Tema representativo de entrevista

Entrevista frontend: ¿Cómo evaluarías la contrapresión (backpressure) y el fallback de WebSocketStream?

FrontendDifícil
Equipo editorial de Offer.ccPublicado Actualizado

Pregunta

Una página colaborativa recibe eventos de alta frecuencia y envía ediciones a un servidor. Evalúa si WebSocketStream es adecuado, explica cómo la contrapresión de Streams, el cierre y la cancelación previenen la acumulación de mensajes, y diseña un fallback para navegadores no compatibles.

Planteamiento y alcance

Una página colaborativa recibe eventos de alta frecuencia y envía ediciones a un servidor. Evalúa si WebSocketStream es adecuado, explica cómo la contrapresión de Streams, el cierre y la cancelación previenen la acumulación de mensajes, y diseña un fallback para navegadores no compatibles.

WebSocketStream es una Web API experimental y no estándar. Expone una conexión como objetos ReadableStream y WritableStream, permitiendo que la contrapresión de Streams regule la lectura y la escritura. MDN recomienda explícitamente verificar la compatibilidad del navegador antes de usarlo en producción. La entrevista trata sobre el diseño de protocolos de flujo y la evaluación de riesgos.

Qué evalúa el entrevistador

Cubre los ciclos de vida de ReadableStream y WritableStream, cómo la contrapresión evita colas ilimitadas, los bloqueos (locks) de lectores y escritores, la diferencia entre abortar y cerrar, el orden e idempotencia de los mensajes, heartbeats y reconexiones, el uso de Worker, la detección de capacidades y el fallback.

Una respuesta de 30 segundos

“Primero confirmo que WebSocketStream no es una funcionalidad ampliamente estandarizada y la habilito solo tras la detección de características. Administro readable y writable de forma independiente, permito que la contrapresión se propague en lugar de almacenar en búfer un arreglo ilimitado, y hago que el cierre, la cancelación y los errores de red sean estados observables. AbortSignal transfiere el ciclo de vida del componente al trabajo asíncrono. En producción se mantiene un adaptador clásico de WebSocket con el mismo protocolo de mensajes, colas delimitadas, retroceso exponencial de reconexión y supresión de duplicados.”

Solución paso a paso

Paso 1: Confirmar capacidad y límites

Verifica si el entorno de ejecución expone WebSocketStream antes de habilitarlo. Es experimental y no estándar, por lo que el soporte en una versión de Chrome no implica soporte en todos los navegadores, WebView o entornos empresariales. Una comprobación de capacidad fallida selecciona directamente la implementación estable.

Paso 2: Abrir la conexión y los flujos

Construye WebSocketStream y espera la promesa opened para obtener readable, writable, el protocolo y la información de extensiones. Consume el lado de lectura a través de un reader y escribe a través de un writer; reporta el cierre de la conexión mediante la promesa closed.

js
const socket = new WebSocketStream(url);
const { readable, writable } = await socket.opened;
const reader = readable.getReader();
const writer = writable.getWriter();

Paso 3: Usar contrapresión (backpressure)

No empujes cada evento a un arreglo normal. Permite que el highWaterMark de un TransformStream o WritableStream downstream limite la cola, espera writer.ready antes de enviar y llama a read al ritmo del consumidor. La contrapresión regula el flujo del cliente; el servidor aún necesita límites de tasa a nivel de protocolo.

Paso 4: Definir el orden de los mensajes

Asigna a las ediciones un número de secuencia, versión del documento y clave de idempotencia. Después de reconectarse, no asumas que el último mensaje en el flujo anterior persistió. Solicita un intervalo faltante o una instantánea versionada del servidor antes de aplicar nuevos eventos, y haz que tanto la capa de renderizado como la de transporte reconozcan duplicados.

Paso 5: Manejar la cancelación y el cierre

Cuando un usuario abandona o cambia de documento, detén las lecturas y escrituras, y luego cierra o cancela los flujos pertinentes. AbortSignal conecta el ciclo de vida del componente con el trabajo asíncrono. El cierre normal, la cancelación activa, el error de protocolo y la pérdida de red deben ser estados distintos para que la lógica de reconexión se aplique solo a fallas de red reales.

Paso 6: Proteger la memoria y la interfaz de usuario

Separa el análisis (parsing), la validación y el renderizado; traslada la decodificación o el procesamiento por lotes a un Worker cuando sea necesario. Establece un tamaño máximo de mensaje, límite de eventos pendientes y tiempo de procesamiento por lotes. Por encima del umbral, pausa la suscripción, combina o descarta eventos intermedios reconstruibles, o solicita una instantánea del servidor en lugar de acumular indefinidamente.

Paso 7: Diseñar la reconexión y recuperación

Utiliza retroceso exponencial con variación aleatoria (jitter) e incluye la última versión confirmada del documento. Después de que el servidor devuelva los eventos faltantes o una instantánea, reanuda el flujo en vivo. Los IDs de idempotencia generados por el cliente evitan que una escritura desconectada se envíe dos veces.

Paso 8: Implementar fallback y observabilidad

Usa WebSocket clásico como fallback predeterminado con el mismo sobre de mensajes y máquina de estados. Registra los resultados de capacidad, la latencia de opened y closed, la profundidad de la cola, la espera de contrapresión, el conteo de reconexiones, los eventos descartados y la tasa de recuperación de instantáneas. Desglósalos por navegador, tipo de red y versión de la página antes de expandir el experimento.

Compensaciones y límites

WebSocketStream o WebSocket

WebSocketStream proporciona contrapresión de flujos y señales de ciclo de vida basadas en Promise, pero el soporte y la estandarización son limitados. El WebSocket clásico está ampliamente disponible, pero requiere que la aplicación limite el trabajo de onmessage y la cola de envío. Una única abstracción de protocolo puede admitir ambos transportes.

Descartar eventos o solicitar una instantánea

Los eventos reconstruibles como el movimiento del cursor se pueden combinar o descartar cuando la cola está llena. Las ediciones de documentos no se pueden descartar silenciosamente; pausa el consumo y solicita una instantánea versionada. La elección depende de la conmutatividad, el orden y el soporte de reproducción en el servidor.

Hilo principal o Worker

Los mensajes pequeños y el renderizado de baja frecuencia se adaptan al hilo principal. La decodificación binaria de alta frecuencia, la compresión y la fusión por lotes pueden trasladarse a un Worker. Un Worker no elimina el límite total de memoria, por lo que las colas de transporte y la cancelación aún necesitan límites explícitos.

Simulacros de fallas y evolución

El navegador no soporta la API

Desactiva la rama de capacidad y verifica que el WebSocket clásico se conecte, consuma los mismos mensajes y reporte métricas de compatibilidad.

El consumidor se ralentiza

Ralentiza artificialmente el renderizado y verifica que la espera de contrapresión aumente, la cola permanezca delimitada y una ruta de recuperación de instantáneas se active en el umbral en lugar de fallar.

La conexión se interrumpe a mitad de una escritura

Desconéctate inmediatamente después de enviar una edición. Verifica que el ID de idempotencia, la última versión confirmada y el retroceso de reconexión restauren el estado sin aplicar la edición dos veces.

Errores comunes y preguntas de seguimiento

Error 1: Asumir que Streams resuelve la limitación del servidor

Pregunta de seguimiento: ¿Qué controla la contrapresión? Controla la producción y el consumo dentro del flujo del cliente; no reemplaza las cuotas del servidor, los límites de conexión ni la agrupación por lotes del negocio.

Error 2: Tratar el cierre, la cancelación y la anulación como idénticos

Pregunta de seguimiento: ¿Qué sucede al desmontar el componente? Registra el cierre normal, la cancelación de lectura y la anulación asíncrona por separado para que la lógica de reconexión se dirija a fallas de red reales.

Error 3: Enviar únicamente WebSocketStream a producción

Pregunta de seguimiento: ¿Qué pasa con una WebView o un navegador más antiguo? Una comprobación de capacidad fallida selecciona el adaptador clásico de WebSocket, mientras que el protocolo de mensajes y la máquina de estados se mantienen iguales.

Preguntas de seguimiento más profundas y respuestas modelo

¿Por qué esperar (await) a writer.ready?

Permite que el emisor espere la capacidad de WritableStream bajo contrapresión en lugar de crear promesas o arreglos ilimitados. Los límites de tasa del servidor y los límites de tamaño de mensaje siguen siendo necesarios.

¿Cuándo debería el cliente solicitar una instantánea?

Cuando la cola de eventos excede su límite, aparece una brecha en la secuencia o el cliente no puede confirmar una versión continua, una instantánea versionada es más segura que adivinar el orden de los eventos.

¿Cómo se despliega una API experimental?

Agrupa por capacidad, versión del navegador y versión de la página, y luego habilítala para un pequeño porcentaje. Compara la profundidad de la cola, la recuperación de desconexiones, la tasa de errores y la latencia de renderizado; desactiva el indicador (flag) y regresa a WebSocket clásico cuando las métricas empeoren.

Fuentes públicas

Preguntas relacionadas