programacion transactions erlang messaging reliability fault-tolerance

transactions - programacion - erlang install



¿Son confiables los mensajes Erlang/OTP? ¿Pueden duplicarse los mensajes? (3)

Creo que la respuesta no tiene nada que ver con Erlang en absoluto. Se basa en la semántica de la interacción Cliente-Servidor donde puede optar por implementar garantías "al menos una vez", "como máximo una vez" o "exactamente una vez" en su protocolo de cliente-servidor. Todas estas semánticas de invocación se pueden implementar combinando etiquetas únicas, reintentos y solicitudes de cliente de registro tanto en el cliente como en el servidor antes de enviarlo o ejecutarlo para que pueda ser recogido por el servidor después del bloqueo. Además de duplicados, puede perderse, mensajes huérfanos o retrasados.

Versión larga:

Soy nuevo en Erlang y considero usarlo para una arquitectura escalable. He encontrado muchos defensores de la plataforma promocionando su confiabilidad y tolerancia a fallas.

Sin embargo, me cuesta entender exactamente cómo se logra la tolerancia a fallas en este sistema donde los mensajes están en cola en la memoria transitoria. Entiendo que se puede organizar una jerarquía de supervisores para reaparecer procesos fallecidos, pero no he podido encontrar mucha discusión sobre las implicaciones de la reaparición en trabajos en curso. ¿Qué sucede con los mensajes en vuelo y los artefactos del trabajo parcialmente completado que se perdieron en un nodo moribundo?

¿Todos los productores retransmitirán automáticamente los mensajes que no se reproducen cuando mueren los procesos de consumo? Si no, ¿cómo se puede considerar esto como tolerante a fallas? Y si es así, ¿qué impide que un mensaje procesado, pero no reconocido, se retransmita y, por lo tanto, reprocesado de forma inapropiada?

(Reconozco que estas preocupaciones no son exclusivas de Erlang, surgen preocupaciones similares en cualquier sistema de procesamiento distribuido. Pero los entusiastas de Erlang parecen afirmar que la plataforma hace todo esto "fácil" ...)

Suponiendo que los mensajes se retransmiten, puedo imaginar fácilmente un escenario en el que los efectos posteriores de una compleja cadena de mensajería podrían volverse confusos después de una falla. Sin algún tipo de sistema de transacción distribuido pesado, no entiendo cómo se puede mantener la consistencia y la corrección sin abordar la duplicación en cada proceso. ¿Debe mi código de aplicación imponer restricciones para evitar que las transacciones se ejecuten más de una vez?

Version corta:

¿Los procesos de erlang distribuidos están sujetos a mensajes duplicados? Si es así, ¿la protección de duplicados (es decir, idempotencia) es una responsabilidad de la aplicación, o erlang / OTP de alguna manera nos ayuda con esto?


El sistema erlang OTP es tolerante a fallas. Eso no te exime de la necesidad de construir aplicaciones igualmente tolerantes a fallas en él. Si usa erlang y OTP, entonces hay algunas cosas en las que puede confiar.

  1. Cuando un proceso muere, ese proceso se reiniciará.
  2. En su mayor parte, un bloqueo de proceso no reducirá toda la aplicación
  3. Cuando se envía un mensaje, se recibirá siempre que el receptor exista.

Por lo que sé, los mensajes en Erlang no están sujetos a duplicación. Si envía un mensaje y el proceso lo recibe, el mensaje se borrará de la cola. Sin embargo, si envía un mensaje y el proceso recibe ese mensaje pero se cuelga mientras lo procesa, ese mensaje se borrará y no se manejará. Ese hecho debe ser considerado en el diseño de su sistema. OTP lo ayuda a manejar todo esto mediante el uso de procesos para aislar el código crítico de la infraestructura (por ejemplo, supervisores, gen_servers, ...) del código de la aplicación que podría estar sujeto a bloqueos.

Por ejemplo, puede tener un gen_server que despache trabajo a un grupo de procesos. Los procesos en el grupo pueden bloquearse y reiniciarse. Pero gen_server se mantiene activo, ya que su único propósito es recibir mensajes y enviarlos al grupo para que trabajen. Esto permite que todo el sistema permanezca activo a pesar de los errores y bloqueos en el grupo y siempre hay algo esperando su mensaje.

El hecho de que el sistema sea tolerante a fallas no significa que su algoritmo sí lo sea.


Voy a separar esto en puntos que espero tengan sentido. Podría estar repitiendo algo de lo que he escrito en la Guía del autostopista para la concurrencia . Es posible que desee leer ese para obtener detalles sobre el fundamento de la forma en que se realiza el envío de mensajes en Erlang.

1. Transmisión del mensaje

El envío de mensajes en Erlang se realiza a través de mensajes asíncronos enviados a buzones (un tipo de cola para almacenar datos). No hay absolutamente ninguna suposición sobre si un mensaje fue recibido o no, o incluso si fue enviado a un proceso válido. Esto se debe a que es plausible suponer [a nivel del lenguaje] que alguien podría querer tratar un mensaje en tal vez solo 4 días y ni siquiera reconocerá su existencia hasta que haya alcanzado cierto estado.

Un ejemplo al azar de esto podría ser imaginar un proceso de larga duración que procesa datos durante 4 horas. ¿Realmente debería reconocer que recibió un mensaje si no puede tratarlo? Tal vez debería, tal vez no. Realmente depende de tu aplicación. Como tal, no se realiza ninguna suposición. Puede tener la mitad de sus mensajes asíncronos y solo uno que no lo es.

Erlang espera que envíe un mensaje de confirmación (y espere un tiempo de espera) si alguna vez lo necesita. Las reglas que tienen que ver con el tiempo de espera y el formato de la respuesta se dejan al programador para especificar: Erlang no puede suponer que desea el acuse de recibo de la recepción del mensaje, cuando se completa una tarea, si coincide o no (el mensaje podría coincidir en 4 horas cuando una nueva versión del código está cargada en caliente), etc.

Para abreviar, si un mensaje no se lee, no se recibe o se interrumpe por alguien que lo desconecta mientras está en tránsito, no importa si no lo desea. Si desea que importe, debe diseñar una lógica en todos los procesos.

La carga de implementar un protocolo de mensajes de alto nivel entre los procesos de Erlang se le da al programador.

2. Protocolos de mensajes

Como dijiste, estos mensajes se almacenan en la memoria transitoria: si un proceso muere, todos los mensajes que no había leído se pierden. Si quieres más, hay varias estrategias. Algunos de ellos son:

  • Lea el mensaje lo más rápido posible y escríbalo en el disco si es necesario, envíe un acuse de recibo y trátelo más tarde. Compare esto con el software de cola como RabbitMQ y ActiveMQ con colas persistentes.
  • Use grupos de procesos para duplicar mensajes en un grupo de procesos en múltiples nodos. En este punto, puede ingresar la semántica transaccional. Este se usa para la base de datos de mnesia para las confirmaciones de transacciones;
  • No suponga que algo ha funcionado hasta que reciba un reconocimiento de que todo fue bien o un mensaje de error
  • Una combinación de grupos de procesos y mensajes de falla. Si un primer proceso no puede manejar una tarea (porque el nodo se cae), la VM envía automáticamente una notificación a un proceso de recuperación que lo maneja en su lugar. Este método a veces se usa con aplicaciones completas para manejar fallas de hardware.

Dependiendo de la tarea que tenga entre manos, puede usar uno o muchos de estos. Todos son posibles de implementar en Erlang y en muchos casos los módulos ya están escritos para hacer el trabajo pesado por usted.

Entonces esto podría responder tu pregunta. Como usted mismo implementa los protocolos, es su elección si los mensajes se envían más de una vez o no.

3. ¿Qué es la tolerancia a fallas?

Escoger una de las estrategias anteriores depende de lo que la tolerancia a fallas signifique para ti . En algunos casos, las personas dicen que "nunca se pierde información, ninguna tarea falla". Otras personas usan tolerancia a fallas para decir "el usuario nunca ve un bloqueo". En el caso de los sistemas Erlang, el significado habitual es mantener el sistema en funcionamiento: está bien que un solo usuario deje caer una llamada en lugar de hacer que todos la abandonen.

Aquí la idea es dejar que las cosas que fallan fallen, pero mantener el resto funcionando. Para lograr esto, hay algunas cosas que la VM le ofrece:

  • Puede saber cuándo un proceso muere y por qué lo hizo
  • Puede obligar a los procesos que dependen el uno al otro a morir juntos si uno de ellos sale mal
  • Puede ejecutar un registrador que registra automáticamente todas las excepciones no detectadas e incluso define su propia
  • Los nodos se pueden monitorear para que sepa cuándo cayeron (o se desconectaron)
  • Puede reiniciar los procesos fallidos (o grupos de procesos fallidos)
  • Tener aplicaciones completas reiniciando en diferentes nodos si uno falla
  • Y muchas más cosas con el marco OTP

Con estas herramientas y algunos de los módulos de la biblioteca estándar que manejan diferentes escenarios para usted, puede implementar casi todo lo que desee además de la semántica asíncrona de Erlang, aunque generalmente vale la pena utilizar la definición de Erlang de tolerancia a fallas.

4. Algunas notas

Mi opinión personal aquí es que es bastante difícil tener más suposiciones que las que existen en Erlang, a menos que quieras una semántica transaccional pura. Uno de los problemas con los que siempre tendrá problemas es cuando los nodos bajan. Nunca se puede saber si se cayeron porque el servidor se bloqueó o porque la red falló.

En el caso de un bloqueo del servidor, simplemente volver a realizar las tareas es bastante fácil. Sin embargo, con una división neta, debe asegurarse de que algunas operaciones vitales no se realicen dos veces, pero tampoco se pierdan.

Por lo general, se reduce al teorema CAP, que básicamente te ofrece 3 opciones, de las cuales debes elegir dos:

  1. Consistencia
  2. Tolerancia de partición
  3. Disponibilidad

Dependiendo de dónde te ubiques, se necesitarán diferentes enfoques. El teorema de CAP generalmente se usa para describir bases de datos, pero creo que se deben hacer preguntas similares siempre que necesite algún nivel de tolerancia a fallas al procesar los datos.