Le pregunté qué es ahora una pregunta eliminada por comunidad en SO sobre por qué alguien usaría JavaScript Promise.race
, y un usuario de alta reputación comentó esto:
Si tiene dos servicios que calculan algún valor, puede consultarlos en paralelo y usar el valor que se devuelva primero, en lugar de consultar uno, esperar un error y luego consultar el segundo.
Busqué en Google la redundancia y este caso de uso en general, pero no pude encontrar nada y, desde mi punto de vista, nunca es una buena idea agregar carga de trabajo a un servidor / servicio si no vas a usar la respuesta.
programming-practices
Adelin
fuente
fuente
Respuestas:
Yo diría que esto es más una cuestión económica. Sin embargo, ese es un juicio que los ingenieros deberían poder hacer. Por lo tanto, estoy respondiendo.
Estoy dividiendo mi respuesta en cuatro partes:
Gestión de riesgos
Entonces, a veces su cliente no puede obtener una respuesta del servidor. Asumiré que esto no se debe a un error programático (de lo contrario, la solución es solucionarlo, así que hazlo). En cambio, debe ser debido a una situación fortuita más allá de su control ...
Pero no más allá de tu conocimiento. Debes saber:
Por ejemplo, si falla y vuelve a intentarlo solo aproximadamente el 2% del tiempo, probablemente no valga la pena abordarlo. Si ocurre alrededor del 80% del tiempo, bueno ... depende ...
¿Cuánto tiempo tiene que esperar el cliente? ¿Y cómo se traduce eso en costos? Verá, tiene una pequeña demora en una aplicación normal, probablemente no sea gran cosa. Si es significativo, y tiene una aplicación en tiempo real o un videojuego en línea, esto alejará a los usuarios y probablemente sea mejor que invierta en más o mejores servidores. De lo contrario, probablemente pueda poner un mensaje de "cargando" o "esperando al servidor". A menos que la demora sea realmente grande (del orden de decenas de segundos), puede ser demasiado incluso para la aplicación normal.
Estrategias
Como dije anteriormente, hay más de una forma de solucionar este problema. Asumiré que ya tiene implementado el bucle try-fail-retry. Entonces, veamos ...
Ahora, note que digo que aún pueden fallar. Si suponemos que una consulta a un servidor tiene un 80% de posibilidades de falla, entonces una consulta paralela a dos servidores tiene un 64% de posibilidades de falla. Por lo tanto, es posible que deba volver a intentarlo.
Una ventaja adicional de elegir el servidor más rápido y seguir usándolo es que el servidor más rápido también tiene menos probabilidades de fallar debido a problemas de red.
Lo que me recuerda, si puede descubrir por qué falla la solicitud, hágalo. Puede ayudarlo a manejar mejor la situación, incluso si no puede evitar las fallas. Por ejemplo, ¿necesita más velocidad de transferencia en el lado del servidor?
Algo mas:
¿Y quién dijo que tienes que hacer solo uno de estos? Puede poner un mensaje de carga, consultar varios servidores que se extienden por todo el mundo para elegir el más rápido y solo usarlo a partir de ahí, en caso de error, vuelva a intentarlo en un bucle y haga que cada uno de esos servidores sea un grupo de máquinas con equilibrio de carga . Por qué no? Bueno, cuesta ...
Costos
Hay cuatro costos:
Tienes que equilibrarlos.
Por ejemplo, digamos que gana aproximadamente un dólar por usuario satisfecho. Que tienes 3000 usuarios por día. Que las solicitudes fallan aproximadamente el 50% del tiempo. Y ese 2% de los usuarios se van sin pagar cuando la solicitud falla. Esto significa que está perdiendo (3000 * 50% * 2%) 30 dólares por día. Ahora, digamos que desarrollar la nueva característica le costará 100 dólares y la implementación de los servidores le costará 800 dólares, e ignorando los costos de tiempo de ejecución, esto significa que tendría un retorno de la inversión en ((100 + 800) / 30 ) 30 dias. Ahora, puede consultar su presupuesto y decidir.
No considere estos valores representativos de la realidad, los elegí por conveniencia matemática.
Addendums:
La cuestión es que si considera el problema en términos de equilibrar costos, puede hacer una estimación del costo de las estrategias que considera y utilizar este análisis para decidir.
Intuición
Intuición si se fomenta por experiencia. No sugiero hacer este tipo de análisis cada vez. Algunas personas lo hacen, y eso está bien. Le sugiero que comprenda algo de esto y desarrolle una intuición.
Además, en ingeniería, además del conocimiento que obtenemos de la ciencia real, también aprendemos en la práctica y compilamos pautas de lo que funciona y lo que no. Por lo tanto, a menudo es aconsejable ver cuál es el estado del arte ... aunque, a veces, necesita ver fuera de su área.
En este caso, miraría los videojuegos en línea. Tienen pantallas de carga, tienen múltiples servidores, elegirán un servidor en función de la latencia e incluso pueden permitir que el usuario cambie de servidor. Sabemos que funciona.
Sugeriría hacer eso en lugar de perder el tráfico de red y el tiempo del servidor en cada solicitud, también tenga en cuenta que incluso con un servidor redundante, puede ocurrir una falla.
fuente
Esto es aceptable si el tiempo del cliente es más valioso que el tiempo en el servidor.
Si el cliente necesita ser rápido y preciso. Puede justificar la consulta de varios servidores. Y es bueno cancelar la solicitud si se recibe una respuesta válida.
Y, por supuesto, siempre es aconsejable consultar a los propietarios / gerentes de los servidores.
fuente
Esta técnica puede reducir la latencia. El tiempo de respuesta del servidor no es determinista. A escala, es probable que haya al menos un servidor que muestre tiempos de respuesta deficientes. Cualquier cosa que use ese servidor, por lo tanto, también tendrá malos tiempos de respuesta. Al enviar a varios servidores, se mitiga el riesgo de hablar con un servidor de bajo rendimiento.
Los costos incluyen tráficos de red adicionales, procesamiento de servidor desperdiciado y complejidad de la aplicación (aunque esto se puede ocultar en una biblioteca). Estos costos pueden reducirse cancelando solicitudes no utilizadas o esperando brevemente antes de enviar una segunda solicitud.
He aquí un artículo , y otro . Recuerdo haber leído un artículo de Google sobre su implementación también.
fuente
Principalmente estoy de acuerdo con las otras respuestas, pero creo que esto debería ser extremadamente raro en la práctica. Quería compartir un ejemplo mucho más común y razonable de cuándo lo usarías
Promise.race()
, algo que usé hace un par de semanas (bueno, el equivalente de Python).Supongamos que tiene una larga lista de tareas, algunas que pueden ejecutarse en paralelo y otras que deben ejecutarse antes que otras. Puede iniciar todas las tareas sin dependencias, luego esperar en esa lista con
Promise.race()
. Tan pronto como se complete la primera tarea, puede comenzar cualquier tarea que dependiera de esa primera tarea, yPromise.race()
nuevamente en la nueva lista combinada con tareas no finalizadas de la lista original. Sigue repitiendo hasta que todas las tareas estén terminadas.Tenga en cuenta que la API de Javascript no está idealmente diseñada para esto. Es prácticamente lo mínimo que funciona, y tienes que agregar un poco de código de pegamento. Sin embargo, mi punto es que funciones como
race()
rara vez se usan para la redundancia. Están principalmente allí para cuando realmente quieres los resultados de todas las promesas, pero no quieres esperar a que se completen antes de tomar acciones posteriores.fuente
new Promise
se llama, y no se reinician cuandoPromise.race()
se llama. Algunas implementaciones prometedoras son flojas, pero las ansiosas son mucho más comunes. Puede probar creando una promesa en la consola que inicie sesión en la consola. Verás registros inmediatamente. Entonces pasa esa promesa aPromise.race()
. Verás que no se registra nuevamente.Además de las consideraciones técnicas, es posible que desee utilizar este enfoque cuando sea parte de su modelo comercial real.
Las variaciones en este enfoque son relativamente comunes en las ofertas en tiempo real de los anuncios. En este modelo, un editor (proveedor de espacio publicitario) solicitará a los anunciantes (proveedores de anuncios) que oferten por una impresión de un usuario en particular. Entonces, para cada impresión, consultaría a cada uno de los anunciantes suscritos, enviando una consulta con los detalles de la impresión a un punto final proporcionado por cada anunciante (o alternativamente, un script proporcionado por el anunciante que se ejecuta como punto final en sus propios servidores), compitiendo todas estas solicitudes hasta un tiempo de espera (por ejemplo, 100 ms) y luego tomar la oferta más alta, ignorando las demás.
Una variación particular de esto que ayuda a reducir el tiempo de espera del cliente es que el editor permita un valor objetivo mínimo para la oferta, de modo que la primera oferta del anunciante que supere ese valor sea aceptada de inmediato (o, si ninguna de las ofertas supera el valor, se tomará el máximo). Entonces, en esta variación, la primera consulta que llega podría ganar y la otra descartada, incluso si son tan buenas o incluso mejores.
fuente