La pila transaccional moderna

La pila transaccional moderna

La pila transaccional moderna PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Las bases de datos transaccionales han sido durante mucho tiempo el componente más crítico del diseño de aplicaciones. ¿Por qué? Porque una base de datos estable es generalmente el último punto de aplicación para la corrección en un mundo desordenado y distribuido. Sin ellos, pagaríamos de más y cobraríamos de menos. Perderíamos pasajeros tratando de llegar a casa desde el aeropuerto y perderíamos artículos en nuestros carritos de compras. Nuestras cuentas en línea se perderían, duplicarían o corromperían y dejarían de funcionar. 

De hecho, la base de datos transaccional (generalmente llamada OLTP, abreviatura de procesamiento de transacciones en línea, base de datos) ha sido tan central para el desarrollo de aplicaciones que, con el tiempo, consumió más y más funciones de aplicaciones. Sin embargo, los microservicios y otras arquitecturas de aplicaciones modernas introdujeron nuevas complejidades en el diseño de aplicaciones: los desarrolladores necesitaban administrar datos en diferentes servicios y garantizar la coherencia entre ellos, lo que los obligó a crear mecanismos internos de procesamiento y sincronización de datos complejos. 

Y así, como industria, estamos viendo una conciencia cada vez mayor de que se necesitan garantías transaccionales fuera del modelo tradicional. estamos viendo el surgimiento de sistemas que extienden sólidas garantías transaccionales más allá de la base de datos, hacia las propias aplicaciones distribuidas

Hemos estado rastreando estas soluciones en los últimos años. En general, se esfuerzan por permitir la gestión transaccional del estado en una gran aplicación distribuida, sin crear desafíos de escalado y al tiempo que brindan un entorno de programación moderno. 

Encontramos que estas soluciones se dividen aproximadamente en dos categorías. Una categoría es orquestación de flujo de trabajo. Básicamente, esto garantiza que un bloque de código se ejecutará hasta su finalización, incluso si falla. Por lo tanto, puede usarse con el propósito de administrar una máquina de estado distribuida de manera determinista sin volverse inestable. La segunda categoría es base de datos + flujo de trabajo, que amplía el diseño de la base de datos OLTP tradicional, lo que permite la ejecución de código arbitrario con el mismo propósito. 

Esta es todavía un área muy incipiente, y hay mucha confusión en torno a la nomenclatura, cómo se usa cada herramienta en la práctica y quién debería usarla. Para ayudar a obtener una mejor comprensión, preguntamos a los profesionales de organizaciones de ingeniería líderes sobre su pila transaccional y cómo piensan sobre tres conceptos clave para las cargas de trabajo transaccionales: estado de la aplicación, lógica comercial y datos comerciales. 

Sin embargo, antes de examinar estas nuevas pilas, aquí hay una breve digresión semitécnica para ayudar a comprender cómo llegamos aquí.

Transacciones, garantías y aplicaciones modernas 

La versión muy aproximada es esta: hay un conjunto de tareas (transacciones) que desea hacer todas o ninguna. Cualquier cosa intermedia (haberlo hecho parcialmente) terminará en un estado corrupto. es dificil de garantizar cualquier cosa en un sistema distribuido, pero las bases de datos lo hacen bien con las transacciones. Por lo tanto, la forma más fácil de manejar las garantías en muchos sistemas es simplemente hacer que la mayoría de las cosas sean transacciones y dejar que la base de datos las maneje.

Las aplicaciones modernas son grandes sistemas distribuidos con muchos usuarios que hacen muchas cosas. Entonces, incluso mantener el estado de la aplicación consistente (como rastrear dónde se encuentran los diferentes usuarios en un flujo de pago) se convierte en un problema de transacción distribuida. En las arquitecturas monolíticas tradicionales, la gestión de transacciones mediante SQL con una base de datos OLTP resultó algo eficaz. Pero en el nuevo y complejo mundo de los microservicios que interactúan a través de API de alto nivel (p. ej., REST o gRPC), las necesidades transaccionales se han vuelto de naturaleza distribuida. 

Sin embargo, muchas empresas que emprenden el viaje hacia los microservicios no han hecho mucho para extender sólidas garantías transaccionales más allá de la base de datos. Y, en la práctica, eso es casi siempre DE ACUERDO. Pero a medida que las aplicaciones escalan, crecen las inconsistencias en los datos, al igual que los errores resultantes y los errores no conciliados en los datos comerciales. Lo cual, por supuesto, puede ser muy problemático. Esto obliga a los desarrolladores de aplicaciones a lidiar con una amplia gama de escenarios de falla y estrategias de resolución de conflictos, y a garantizar la consistencia del estado al idear sus propias estrategias a través de diferentes patrones arquitectónicos.

Definiciones

Datos comerciales ("datos") se refiere a los datos críticos para el negocio tradicionalmente almacenados en una base de datos OLTP para persistencia y procesamiento (por ejemplo, información de perfil de usuario como nombre, dirección, puntaje de crédito, etc.).

Estado de la aplicación se refiere al estado actual del sistema; el estado de la aplicación está determinado por un valor almacenado en un sistema de almacenamiento de datos y en qué paso se encuentra la ejecución del programa en una máquina de estados finitos (por ejemplo, el estado de un pedido, como "pedido recibido", "inventario verificado", "crédito verificado ”, “enviado”, “devuelto”).

Lógica de negocios se refiere a la parte del programa que se ocupa de cómo funciona realmente la aplicación o qué hace, en lugar de los detalles de ejecución (p. ej., "Si los ingresos_del_usuario > $100 y la puntuación crediticia >650 ⇒ hipoteca_aprobada = VERDADERO").

Para los fines de esta discusión, es importante distinguir el estado de la aplicación y los datos comerciales. Por ejemplo, saber que un cliente ha ingresado su tarjeta de crédito pero no ha realizado el pago es el estado de la aplicación. Los datos de la tarjeta de crédito y los artículos en el carrito de la aplicación son los datos comerciales. 

La pila transaccional moderna PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

En un flujo típico, una solicitud proviene del front-end, se autentica y luego se enruta a través de una puerta de enlace API o GraphQL al punto final relevante. 

Ese único punto final de API ahora tiene que orquestar decenas o cientos de microservicios para entregar la transacción comercial al cliente final. Aquí es donde los desarrolladores suelen agrupar todo en blobs de lógica empresarial y luego usan una combinación de colas, cachés y mecanismos de reintento codificados a mano para llevar los datos a la base de datos, con suerte comprometidos como una transacción completa.

A medida que aumenta la escala de la aplicación, también lo hace la complejidad de la gestión de colas y cachés, así como la cantidad de aristas en la lógica de reconciliación cuando surgen problemas. 

El auge de las pilas transaccionales centradas en el flujo de trabajo y en la base de datos

OK, entonces las transacciones son importantes. LAMP en una base de datos no era suficiente para escalar. Y una bola de pelo gigante de colas y lógica de reintento es demasiado frágil. Para hacer frente a esto, hemos visto, en los últimos años, la aparición de nuevas soluciones que devuelven la cordura a la lógica transaccional. Se pueden clasificar aproximadamente como enfoques centrados en el flujo de trabajo o enfoques centrados en la base de datos.

Hasta la fecha, los motores de flujo de trabajo funcionan principalmente en el estado de la aplicación en lugar de en los datos comerciales y, a menudo, requieren cierta complejidad cuando se integran con bases de datos tradicionales. Los enfoques centrados en bases de datos agregan lógica de aplicación junto con datos comerciales, pero aún no tienen la misma sofisticación de ejecución de código de los motores de flujo de trabajo. 

El siguiente diagrama proporciona un bosquejo aproximado de cómo se utilizan los enfoques centrados en el flujo de trabajo y/o la base de datos en una aplicación Javascript/Typescript, suponiendo que ambos estén en uso. Si bien hoy en día son piezas distintas de esta arquitectura, hemos visto los primeros signos de una tendencia en la que las bases de datos están incorporando funciones de flujo de trabajo y los flujos de trabajo están comenzando a adoptar un almacenamiento duradero. Esta fusión de capacidades indica que las líneas entre los dos enfoques se están desdibujando y se están volviendo menos distintas en las arquitecturas modernas. 

La pila transaccional moderna PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Enfoques centrados en el flujo de trabajo en detalle 

Un flujo de trabajo son simplemente bloques de código que se ejecutan en función de eventos o temporizadores que hacen evolucionar la máquina de estado de la aplicación. El flujo de trabajo transaccional asegura la ejecución del código con fuertes garantías, evitando estados parciales o no deseados en la aplicación. Los desarrolladores escriben la lógica y el motor de flujo de trabajo maneja transacciones, mutaciones e idempotencia. Los diferentes motores de flujo de trabajo hacen diferentes concesiones en cuanto a la cantidad de detalles de la transacción que se exponen a los desarrolladores. 

A modo de ejemplo, a continuación se muestra una representación visual de un flujo de trabajo de pago que se ejecuta en Orkes (Conductor): 

La pila transaccional moderna PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Existen dos enfoques aproximados por el cual los motores de flujo de trabajo ganan tracción. En uno (tipificado por Temporal.io), los desarrolladores escriben código utilizando lenguajes de programación back-end estándar (por ejemplo, Go o Java) y el el sistema se asegurará de que el código se ejecute hasta su finalización, incluso durante una falla. En este modelo, la pila de llamadas al programa se mantiene incluso si el código está esperando que se complete una llamada de bloqueo (por ejemplo, lectura o escritura). Para hacer esto, el tiempo de ejecución del lenguaje se modifica para evitar la ejecución parcial de código durante las fallas. La ventaja de este enfoque es que los desarrolladores pueden escribir en lenguajes familiares y depurar fácilmente con una pila de llamadas mantenida. Vemos que este enfoque es más popular entre los equipos de back-end que se ocupan de aplicaciones grandes y sofisticadas. 

La desventaja es que a menudo requiere mucho trabajo de integración y código contenedor para exponer interfaces útiles y seguras para los desarrolladores de aplicaciones. Otro inconveniente es que se basa en una capa de ejecución personalizada en lugar del lenguaje básico, y hay casos extremos en los que la ejecución diferirá del tiempo de ejecución del idioma nativo. Por lo tanto, si bien los desarrolladores pueden usar lenguajes con los que están familiarizados, aún deben comprender cómo funciona el sistema subyacente.  

El otro enfoque, que es más popular entre los desarrolladores de aplicaciones (particularmente Typescript/Javascript) es que el motor de flujo de trabajo servir como orquestador de funciones asíncronas (por ejemplo, Inngest, Defer y Trigger). En este modelo, los eventos o funciones de terceros se dirigen al motor de flujo de trabajo, que luego enviará la lógica registrada por los programadores de la aplicación, quienes deben devolver el control una vez que surja la necesidad de bloquear otra función asíncrona. La ventaja es que este es un método mucho más ligero de integración en un programa. También impone suficiente estructura en el código para que el equipo que trabaja en él pueda entenderlo más fácilmente. Sin embargo, este enfoque puede ser más difícil de depurar sin soporte de herramientas, por lo que la depuración tiende a ser específica de la plataforma.

Los motores de flujo de trabajo son particularmente poderosos porque permiten la adopción gradual por parte de las aplicaciones existentes. Se pueden aplicar poco a poco a ciertos flujos de trabajo con una huella mínima. Dicho esto, las dos mayores deficiencias de los motores de flujo de trabajo se derivan del hecho de que no se extienden a la base de datos. Como resultado, no existe una única fuente de información consultable sobre el estado de la aplicación y los datos empresariales. Además, la semántica transaccional generalmente es diferente de la semántica de la base de datos, lo que requiere que los desarrolladores de aplicaciones manejen las condiciones de borde. 

Aunque no es la norma hoy en día, queremos ilustrar las arquitecturas conceptuales de cómo los flujos de trabajo se pueden usar en muchos casos como almacenes de datos persistentes:

Ejemplos de arquitecturas solo de flujo de trabajo

La pila transaccional moderna PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Arquitectura solo de flujo de trabajo: aplicaciones de JavaScript

La pila transaccional moderna PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

Arquitectura de solo flujo de trabajo: aplicaciones que usan microservicios

Enfoques centrados en bases de datos en detalle 

Los enfoques centrados en bases de datos comienzan con una base de datos, pero la amplían para admitir la ejecución de código arbitrario para permitir flujos de trabajo junto con la gestión de datos. Hacen esto dando control a los programadores para que puedan tomar decisiones explícitas sobre mutaciones, transacciones e idempotencia para bloques de código regulares, esencialmente al exponer la semántica OLTP directamente. El programador es responsable de mantener la lógica comercial y los datos comerciales separados del estado de la aplicación. 

De hecho, la vista de la base de datos pura es que el estado de la aplicación siempre se puede derivar de los datos comerciales. Esto generalmente se hace almacenando el estado de la aplicación como un conjunto de transacciones que modifican los datos comerciales en la base de datos. Es más fácil pensar en esto como una base de datos que puede ejecutar bloques de código con las mismas garantías sólidas que los sistemas de flujo de trabajo descritos anteriormente. 

Internamente, llamamos a esto el plataforma transaccional de lógica de aplicación (ALTP) porque, en última instancia, extiende las transacciones OLTP a la aplicación. Pero lo que realmente caracteriza a ALTP es que, para las aplicaciones completamente nuevas, puede obviar por completo la necesidad de que los desarrolladores de aplicaciones administren directamente la infraestructura de back-end.  

Desde el punto de vista de ALTP, el enfoque más utilizado comenzó con Firebase, que ofrece una "experiencia de back-end" de servicio completo incluyendo autenticación, almacén de datos, bases de datos y más. Firebase y los participantes más recientes, como Supabase, siguen siendo plataformas muy populares para proyectos nuevos. Y aunque tienden a permanecer fieles a sus raíces OLTP, y por lo tanto no admiten la ejecución de código arbitrario para funciones de back-end transaccionales, Supabase ya está comenzando a agregar soporte para flujos de trabajo.

Sin embargo, ofertas ALTP de próxima generación como Convex permiten la ejecución de código arbitrario como una transacción junto con la base de datos. Estas ofertas permiten escribir código totalmente compatible con transacciones en un lenguaje normal (por ejemplo, Javascript/Typescript), donde un solo bloque de código puede leer, escribir y mutar datos, tanto el estado de la aplicación como los datos comerciales. En cierto sentido, brinda a los desarrolladores una única fuente de información consultable y proporciona primitivas de flujo de trabajo como suscripciones. 

ALTP resuelve el problema que tienen los motores de flujo de trabajo al desacoplarse de la base de datos, pero, como resultado, requiere que los usuarios confíen en su oferta de base de datos en lugar de un OLTP estándar para obtener los beneficios. Como resultado, vemos principalmente que los equipos adoptan ALTP para aplicaciones totalmente nuevas, en lugar de integrarlo en backends complejos existentes.

La pila transaccional moderna PlatoBlockchain Data Intelligence. Búsqueda vertical. Ai.

El diagrama anterior es una amalgama de los muchos operadores con los que hablamos. Algunos solo usarán un motor de flujo de trabajo. Algunos simplemente usarán un enfoque centrado en la base de datos. Pero muchos usarán ambos, especialmente cuando recién comienzan a adoptar flujos de trabajo. Los usuarios de motores de flujo de trabajo de hoy tienden a ser equipos de back-end que se ocupan de aplicaciones grandes y complejas, aunque también hemos visto que muchos equipos completos los adoptan. Las soluciones de back-end como servicio tienden a ser más fáciles de usar para los desarrolladores de aplicaciones y se usan más comúnmente cuando la aplicación impulsa la selección de tecnología. 

la convergencia

Cada vez es más claro que los enfoques centrados en el flujo de trabajo y los enfoques centrados en la base de datos están en curso de colisión. La razón principal de esto es que, si bien el estado de la aplicación y el estado de la base de datos son lógicamente distintos, dependen el uno del otro, y un sistema que no cubre ambos es complejo de corregir y depurar.  

Como ejemplo, considere un motor de flujo de trabajo que se usa para rastrear la máquina de estado para el proceso de pago de un usuario, y ese usuario está agregando un artículo a un carrito. Por lo general, los motores de flujo de trabajo garantizan que un paso de código se ejecutará incluso en caso de falla. Sin embargo, puede haber instancias en las que el motor necesite volver a ejecutar un paso dado durante una falla porque no está completamente seguro de si el paso se completó por completo. Si ese paso implica escribir datos comerciales en una base de datos tradicional (en este caso, el artículo en el carrito) y la base de datos no reconoce el reintento duplicado, terminará con una entrada duplicada. 

Hay dos maneras de lidiar con esto. Una forma es enviar el problema al desarrollador de la aplicación, que utilizará un nonce proporcionado por el sistema de flujo de trabajo para garantizar que solo se escriba un elemento. Pero eso supone que el desarrollador comprende la idempotencia, que es notoriamente difícil de hacer bien, y esto obvia gran parte de la magia de tener un sistema de flujo de trabajo. La otra forma es vincular el motor de flujo de trabajo a una base de datos que conozca la semántica transaccional del flujo de trabajo. Esto aún no ha sucedido, pero no es difícil creer que sucederá. 

Por otro lado, los enfoques centrados en bases de datos se dan cuenta de que el flujo de trabajo general es realmente útil para los desarrolladores de aplicaciones. Y así, estamos empezando a ver bases de datos (como Convex), que admiten funciones de bases de datos tradicionales como consultas, mutaciones, índices, etc., implementan funcionalidades como programación y suscripciones. Estos les permiten ser utilizados como motores de flujo de trabajo. Es decir, permiten la ejecución de bloques de código arbitrario con fuertes garantías. 

Como lo expresó Ian Livingstone (quien brindó comentarios sobre este artículo): “Es el clásico '¿Traes la lógica de la aplicación a la base de datos o la base de datos a la lógica de la aplicación?' jugando de nuevo... esta vez provocada por la ruptura del monolito". Habiendo tenido esa dicotomía durante décadas, está claro que ambos modelos persistirán en el corto plazo. Es mucho menos claro que seguirá siendo así a largo plazo. 

Un agradecimiento especial a Charly Poly (Defer), Dan Farrelly (Inngest), David Khourshid (Stately), Ian Livingstone (Cape Security), Enes Akar (Upstash), James Cowling (Convex), Jamie Turner (Convex), Paul Copplestone (Supabase ), Sam Lambert (PlanetScale), Tony Holdstock-Brown (Inngest), Matt Aitken (Trigger) por revisar esta publicación y brindar sus comentarios. Además, gracias a Benjamin Hindman (Reboot), Fredrik Björk (Grafbase), Glauber Costa (Chiselstrike), Guillaume Salles (Liveblocks), Maxim Fateev (Temporal), Steven Fabre (Liveblocks) y Viren Baraiya (Orkes) por ayudarnos con la investigación.

* * *

Las opiniones expresadas aquí son las del personal individual de AH Capital Management, LLC ("a16z") citado y no son las opiniones de a16z o sus afiliados. Cierta información contenida aquí se ha obtenido de fuentes de terceros, incluso de compañías de cartera de fondos administrados por a16z. Si bien se tomó de fuentes que se consideran confiables, a16z no ha verificado de forma independiente dicha información y no hace declaraciones sobre la precisión duradera de la información o su idoneidad para una situación determinada. Además, este contenido puede incluir anuncios de terceros; a16z no ha revisado dichos anuncios y no respalda ningún contenido publicitario incluido en ellos.

Este contenido se proporciona solo con fines informativos y no debe considerarse como asesoramiento legal, comercial, de inversión o fiscal. Debe consultar a sus propios asesores sobre estos asuntos. Las referencias a cualquier valor o activo digital son solo para fines ilustrativos y no constituyen una recomendación de inversión ni una oferta para proporcionar servicios de asesoramiento de inversión. Además, este contenido no está dirigido ni destinado a ser utilizado por ningún inversionista o posible inversionista, y bajo ninguna circunstancia se puede confiar en él al tomar una decisión de invertir en cualquier fondo administrado por a16z. (Una oferta para invertir en un fondo a16z se realizará solo mediante el memorando de colocación privada, el acuerdo de suscripción y otra documentación relevante de dicho fondo y debe leerse en su totalidad). Cualquier inversión o compañía de cartera mencionada, referida o descritos no son representativos de todas las inversiones en vehículos administrados por a16z, y no puede garantizarse que las inversiones serán rentables o que otras inversiones realizadas en el futuro tendrán características o resultados similares. Una lista de inversiones realizadas por fondos administrados por Andreessen Horowitz (excluyendo inversiones para las cuales el emisor no ha otorgado permiso para que a16z divulgue públicamente, así como inversiones no anunciadas en activos digitales que cotizan en bolsa) está disponible en https://a16z.com/investments /.

Los cuadros y gráficos proporcionados en el interior tienen únicamente fines informativos y no se debe confiar en ellos al tomar cualquier decisión de inversión. El rendimiento pasado no es indicativo de resultados futuros. El contenido habla sólo a partir de la fecha indicada. Todas las proyecciones, estimaciones, pronósticos, objetivos, perspectivas y/u opiniones expresadas en estos materiales están sujetas a cambios sin previo aviso y pueden diferir o ser contrarias a las opiniones expresadas por otros. Consulte https://a16z.com/disclosures para obtener información adicional importante.

Sello de tiempo:

Mas de Andreessen Horowitz