Documentation for a newer release is available. View Latest

Cómo maneja IPF la realidad

Introducción

Posiblemente la definición más citada de sistemas distribuidos fue dada convenientemente por uno de los fundadores del campo:

A distributed system is one in which the failure of a computer you didn’t even know existed can render your own computer unusable.
— Leslie Lamport

Por desgracia, esa definición sigue vigente: la computación distribuida es un campo muy complejo que a menudo conduce a conceptos erróneos y suposiciones falsas.

Las ya legendarias falacias que L. Peter Deutsch y otros en Sun Microsystems identificaron siguen siendo prevalentes y pueden llevar a problemas significativos si no se abordan adecuadamente. Este documento introduce las falacias y explica cómo puedes intentar no caer en ellas cuando uses el IPF SDK.

Las falacias de la computación distribuida

  1. The network is reliable.

  2. Latency is zero.

  3. Bandwidth is infinite.

  4. The network is secure.

  5. Topology doesn’t change.

  6. There is one administrator.

  7. Transport cost is zero.

  8. The network is homogeneous.

Abordando las falacias con IPF SDK

1. The Network is Reliable

A veces, las aplicaciones de software se escriben con poca o ninguna consideración por los errores de red. Durante una interrupción de la red, tales aplicaciones pueden bloquearse o esperar infinitamente una respuesta, consumiendo permanentemente memoria u otros recursos. Cuando la red que falló vuelve a estar disponible, esas aplicaciones también pueden fallar al reintentar operaciones en pausa o requerir un reinicio (manual).

Enfoque IPF

IPF usa Akka y, como indica su documentación oficial, emulando Erlang, Akka hace explícita la falibilidad de la comunicación mediante el paso de mensajes; por lo tanto, no intenta mentir y emular una abstracción con fugas y el IPF SDK ha hecho suya esta filosofía.

Al comunicarse con flows construidos por IPF DSL, los desarrolladores de IPF siempre deben considerar fallos de red. El código generado por IPF DSL proporciona reintentos configurables y controles de idempotencia por defecto, dando a los desarrolladores mecanismos para construir semánticas de entrega exactamente-una-vez cuando se trata de inputs de flow.

Además, IPF DSL permite transiciones de flow en timeouts, promoviendo así la gestión de timeouts a ciudadano de primera clase: a menudo, qué hacer en caso de timeouts causados por ausencia de respuesta de un sistema externo es una decisión de negocio y no técnica. Configurando timeouts, los desarrolladores pueden crear diversas políticas de timeouts y asegurar así que se cumplan las demandas de negocio ante problemas de red.

Dado que en una aplicación típica de orquestación IPF los flows se integran estrechamente con el IPF Connector Framework, aprovechar retries y timeouts tanto en send como en receive connectors puede ayudarte a lidiar con interrupciones temporales de red, mientras que para interrupciones más largas puede ser más aplicable una estrategia de reintentos basada en mensajes enviados a un dead letter appender.

Akka Cluster también viene con soporte incorporado para particiones de red (y caídas de máquina, que se tratan igual que inalcanzable por red):

  • Akka Failure Detector monitoriza la salud de nodos remotos y detecta fallos usando el algoritmo phi accrual, permitiendo al sistema responder apropiadamente. El detector usa heartbeats para comprobar la disponibilidad de nodos.

  • Si un nodo no responde a heartbeats en un periodo, se marca como inalcanzable y Akka Split Brain Resolver toma acciones apropiadas como apagar nodos en el lado minoritario de la partición, redirigiendo mensajes o redistribuyendo tareas a los nodos restantes.

Por último, debes aplicar a tus flows las mismas prácticas de desarrollo que a tus funciones: prefiere flows de orquestación más cortos que hagan una cosa bien. Flows complejos —que constan de muchos estados y elevan muchos domain events durante su vida— a menudo tardan más en recuperarse cuando Akka los distribuye a nuevos nodos. Una buena regla es apuntar a flows con no más de 20 estados; por ejemplo, si realizar un chequeo de sanciones en tu flow de pagos consta de 8 estados, considera extraer esa lógica a un flow propio, especialmente si es algo reutilizable por otros flows.

Con todas las opciones de configuración de reintentos y timeouts disponibles a distintos niveles, es fácil crear una configuración que cause más daño que beneficio. Como regla general deberías:

  • empezar sin timeouts en tus receiving connectors

  • asegurar que el attempt timeout de un sending connector sea suficientemente más corto que su call timeout si quieres que el connector realice reintentos

  • asegurar que el call timeout del sending connector adecuado sea más corto que el action retry interval más pequeño; de lo contrario, el flow puede rendirse demasiado pronto en un intento y causar que tu aplicación cree demasiadas solicitudes downstream

2. Latency is Zero

Los desarrolladores a menudo escriben software que asume que la comunicación de red ocurre instantáneamente, sin demora. En realidad, siempre hay latencia por el tiempo que tarda en viajar la data por la red, influenciado por la distancia, congestión, y el tiempo de procesamiento de dispositivos intermedios y sistemas externos de destino. Esto puede provocar cuellos de botella de rendimiento cuando se despliega en un entorno distribuido: las aplicaciones pueden retener recursos críticos del sistema como threads mientras esperan que las operaciones completen, o iniciar más trabajo del que puede completarse en un tiempo dado, consumiendo más y más recursos hasta que se agotan y la aplicación se detiene o finalmente cae.

Enfoque IPF

IPF Connector Framework está construido sobre Akka Streams, que —similar a otras implementaciones de Reactive Streams— proporcionan mecanismos de back-pressure para manejar latencias variables y asegurar un flujo de datos suave. Akka Streams permite definir pipelines de procesamiento de datos que pueden adaptarse a la velocidad del componente más lento, evitando saturar cualquier parte del sistema y asegurando procesamiento eficiente incluso con latencia.

Además, tanto Akka como IPF SDK se han construido alrededor de operaciones no bloqueantes, asegurando que no se desperdicien recursos del sistema esperando I/O, sino que se usen para procesar trabajo pendiente.

Dado que rara vez las operaciones de negocio querrán esperar para siempre a que una operación termine, al considerar la latencia en tus soluciones debes aplicar el mismo pensamiento de reintentos y timeouts ya descrito en Network is Reliable.

Finalmente, a menudo la mejor forma de lidiar con la latencia de red es no usar la red: cachear localmente datos usados frecuentemente en lugar de hacer llamadas remotas repetidas suele ser una gran manera de reducir el impacto de la latencia. IPF proporciona su propia biblioteca de cache in-memory, pero no impide usar cualquier otra tecnología de caching que prefieras. Cuidado con el viejo dicho:

There are only two hard things in Computer Science: cache invalidation and naming things.
— Phil Karlton
IPF viene con soporte integrado para telemetría, de la cual las más fáciles de usar son las métricas de Akka, aplicación y connector, todas las cuales incluyen latencias de diferentes partes del sistema.

3. Bandwidth is Infinite

Los desarrolladores de software a menudo permiten tráfico sin límites hacia y desde sus aplicaciones sin considerar los límites de bandwidth, causando en el peor caso congestión que añade latencia a la red, e incluso desperdiciando bandwidth aumentando los paquetes descartados por desbordes de buffer.

Enfoque IPF

Muy similar a Latency is Zero, los mecanismos de back-pressure existentes permitirán que las soluciones con IPF SDK se ralenticen cuando la latencia aumente debido a que el bandwidth lleve la red al límite. Pero el IPF SDK también ofrece enfoques proactivos para controlar el bandwidth antes de que empiece a causar problemas:

  • Send connector circuit breakers pueden usarse para aliviar proactivamente tráfico hacia sistemas downstream: una vez que la latencia supere el attempt timeout configurado del connector, los circuit breakers fallarán rápidamente y omitirán transmitir por la red.

  • Message throttling tanto en send como en receive connectors te permite establecer límites arbitrarios en la cantidad de mensajes que estás dispuesto a enviar o recibir en un intervalo de tiempo dado, aplicando back-pressure cuando se exceden los límites.

  • IPF Connector conversion stages permiten usar cualquier protocolo de serialización/deserialización que se ajuste a tus necesidades; elegir un protocolo más eficiente que el actual puede ayudar a reducir el bandwidth. Opciones populares incluyen CBOR, Avro, Protocol Buffers y Ion, pero incluso pasar a una representación binaria de JSON como Smile puede dar resultados.

  • Revisa tus flows y busca oportunidades para reducir la cantidad de datos incluidos en los domain events:

    • Asegúrate de que no haya redundancia de datos en los domain events, p. ej., no declares un business data element en el domain event si su valor realmente no cambia.

    • Declara solo los business data elements que tus flows estén usando activamente; p. ej., no intentes incluir datos para auditoría, ya que eso suele manejarse fuera de los flows de orquestación.

  • Habilitar compresión en distintos niveles.

    • Al persistir domain events de IPF DSL o al comunicarte con diferentes actores de Akka (incluidos flows de IPF DSL), se utiliza Akka Serialization para serializar datos antes del transporte y algunos serializers disponibles, como el Jackson Serializer (predeterminado en IPF), ofrecen capacidades de compresión.

    • MongoDB, Kafka y muchos servidores HTTP en Java soportan compresión a nivel de transporte que puedes habilitar para reducir el tamaño de los payloads intercambiados por la red.

  • Aprovechar caching. Como se mencionó, IPF viene con su biblioteca de cache in-memory, pero puedes usar la tuya. Para ayudar a reducir el bandwidth general, puedes aplicar caching también fuera de IPF: los balanceadores de carga y service meshes ofrecen soporte de cache, al igual que las CDNs.

  • Re-arquitecta algunos componentes de tu aplicación para que sean local-first. Como se describe en otros lugares, IPF SDK se basa en event sourcing, por lo que es posible suscribirse a los domain events emitidos por los flows y construir vistas materializadas locales (eventualmente consistentes) y proyecciones.

    Aunque el enfoque local-first puede adoptarse para mejorar la resiliencia y disponibilidad de tu aplicación, si lo adoptas solo por las mejoras de bandwidth tienes que asegurarte de que la cantidad de datos que necesitas leer de servicios externos es mayor que los event data a los que te suscribes; de lo contrario, podrías terminar aumentando el bandwidth.
Asegúrate de incluir métricas de red detalladas cuando configures la observabilidad de tus despliegues de aplicaciones IPF.

4. The Network is Secure

A menos que la seguridad sea una de las características principales del sistema desarrollado, a menudo es un pensamiento posterior en muchos ciclos de desarrollo de software, lo que conduce a vulnerabilidades.

Enfoque IPF

Asumir que la red es segura a menudo lleva a descuidar el cifrado de datos en tránsito; sin cifrado, actores maliciosos pueden interceptar información sensible. Aunque una aplicación típica construida con IPF SDK normalmente no se ejecute dentro de una DMZ, aún hay decisiones conscientes de seguridad que puedes tomar para reducir riesgos.

Akka Remoting — el componente usado internamente por Akka Cluster y por IPF para comunicarse con los flows de IPF SDK— soporta cifrado a nivel de transporte, con el bonus añadido de soportar mTLS con certificados rotados frecuentemente cuando se ejecuta en Kubernetes.

Además de soportar cifrado a nivel de transporte en Akka, IPF SDK también permite añadir cifrado a nivel de transporte a otros componentes:

Por último, si los consumidores destino lo soportan, IPF Connectors permiten enviar y consumir payloads cifrados mediante stages de cifrado. Usar payloads cifrados con JMS y Kafka también asegura que los datos estén cifrados en reposo.

Incluir pruebas de seguridad y de penetración en tu pipeline de CD es buena idea, pero siempre consulta expertos en seguridad (internos o externos) para realizar periódicamente una auditoría de seguridad adecuada: podría salvarte de salir en titulares por las razones equivocadas.

5. Topology Doesn’t Change

Con la virtualización generalizada de servidores y la contenedorización de aplicaciones, la analogía Pets vs Cattle está empezando a favorecer fuertemente el enfoque cattle, lo que significa que iniciar, detener y que desaparezcan máquinas o instancias de una aplicación es un hecho cotidiano. Los sistemas mal diseñados que no tienen en cuenta la redundancia y la tolerancia a fallos son más vulnerables a cambios en la topología de red y de máquinas. Si falla un único nodo o enlace, todo el sistema puede detenerse.

Enfoque IPF

Como se mencionó, IPF SDK se apoya en Akka Cluster y sus plugins para bootstrap del clúster del servicio y luego adaptarse dinámicamente a cambios en la topología de red y máquinas, asegurando disponibilidad continua y escalabilidad. Akka Cluster y sus mecanismos de discovery (incluyendo el descubrimiento basado en MongoDB propiedad de IPF) pueden detectar automáticamente cuando los nodos se unen o abandonan el clúster y redistribuir datos y tareas en consecuencia. Esto asegura que el sistema permanezca operativo incluso cuando cambia la topología de red.

Si bien los componentes mencionados cubren cambios de topología "gráciles", al tratar con cambios no gráciles —es decir, caídas y fallos— la pérdida de mensajes, ausencia de respuesta o aumento de latencia son síntomas comunes, por lo que se tratan de forma similar a lo descrito en Network is Reliable y Latency is Zero.

Además, dado que IPF SDK pretende soportar la escritura de aplicaciones cloud-native por diseño, ofrece herramientas para ayudarte al desplegar en la nube:

  • APIs HTTP para health, liveness y readiness mediante Spring Boot Actuator y Akka Management HTTP que pueden usar diversas plataformas para saber cuándo enrutar tráfico a tu instancia de aplicación de forma segura o cuándo eliminar o reiniciar una instancia no respondiente o caída.

  • Connector Operations API para permitir construir automatización alrededor de iniciar y detener recepción de tráfico.

  • Soporte de rolling upgrades para permitir evitar downtime al desplegar nuevas versiones de tus aplicaciones.

6. There is One Administrator

Particularmente frecuente cuando falta comunicación entre equipos de infraestructura y desarrollo, la suposición de los equipos de desarrollo de que hay un único administrador donde todo el conocimiento está centralizado y es consistente puede llevar a fallos inesperados cuando se producen conflictos y misconfiguraciones.

Enfoque IPF

A diferencia de las falacias anteriores, IPF SDK juega solo un papel de apoyo para ayudarte a superar los problemas de esta: el papel principal lo desempeñan las herramientas y prácticas de observabilidad y monitorización que hayas adoptado. Como se mencionó en Topology Doesn’t Change y Latency is Zero, IPF viene con amplio soporte de observabilidad, así como endpoints HTTP amigables con la automatización y cloud-native. Estos no solo te permiten hacer seguimiento de tus SLAs y KPIs, sino que también pueden ser usados por herramientas de alertas y monitorización para ayudarte a añadir automatización robusta en pipelines de CI/CD. Cuando tu CI/CD incluye todas tus aplicaciones y también abarca infraestructura, se vuelve posible realizar rollbacks automáticos de cambios breaking. Incluso sin automatización, la telemetría de IPF y Akka debería permitirte construir reglas de alerta y dashboards operativos que ayuden a identificar problemas.

Como parte de tus prácticas operativas, debes esforzarte por automatizar todo lo que pueda automatizarse. Para áreas que no puedan, construye buenas alertas y dashboards para que tus operadores tengan más fácil localizar problemas.

7. Transport Cost is Zero

Ya sea que construyas y gestiones tus propios datacenters o uses un proveedor cloud, siempre hay un coste asociado a la comunicación de red. Desde la perspectiva del software ese coste se paga con sobrecarga computacional y latencia de llamadas remotas, pero desde la operativa el coste también es financiero. Olvidar este factor de coste durante diseño y desarrollo puede convertir una gran idea de mejora de negocio en un gran pozo de dinero.

Enfoque IPF

Tu enfoque aquí será reducir el número de llamadas de red, el tamaño de los payloads enviados por el cable y mejorar la localidad de datos. Si todo esto te suena, ¡has estado prestando atención! El enfoque aquí es similar a cómo se maneja Bandwidth is Infinite. La única diferencia es qué rastreas en tus herramientas de monitorización e informes: querrás identificar las métricas que se correlacionan con tus costes y usarlas primero para guiar optimizaciones y luego para vigilar cuidadosamente el presupuesto. Para despliegues en la nube, quizá quieras centrarte en infraestructura de red dedicada como NAT gateways y comunicación entre datacenters, ya que normalmente incurren en costes significativos de transferencia de datos.

8. The Network is Homogeneous

Los desarrolladores frecuentemente asumen que todas las partes de una red son uniformes en rendimiento, fiabilidad y configuración, lo cual puede contrastar con la realidad incluso en redes locales pequeñas o medianas, y más aún en las enormes redes privadas gestionadas por proveedores cloud.

Enfoque IPF

Esta falacia fue una adición tardía a la lista por James Gosling y, en cierto modo, une todas las anteriores. La realidad es que las redes pueden incluir una mezcla de hardware, software, protocolos, configuraciones y costes, y todo ello puede cambiar en cualquier momento en la vida de tu aplicación.

Por lo tanto, las aplicaciones que construyas deben ser capaces de manejar network partitions o destinos que se vuelven inalcanzables, latencias cambiantes y congestiones repentinas, o requisitos de seguridad que se vuelven más estrictos de la noche a la mañana.

Conclusión

El IPF SDK, con su dependencia de varios módulos de Akka, proporciona una solución moderna y completa a las realidades de trabajar con arquitecturas modernas. Aprovechando estas tecnologías, los desarrolladores pueden construir con mayor facilidad sistemas distribuidos robustos, escalables y fiables que aborden los desafíos inherentes de la computación distribuida.

Como se ha mencionado varias veces en este documento, el IPF SDK no está hecho para funcionar en aislamiento: facilita la construcción de aplicaciones 12-factor y cloud-native y, para sacarle el máximo partido, debe integrarse completamente en tu ecosistema operativo existente e incluirse en las herramientas de alertas y monitorización relevantes que uses.