Aprende cómo ejecutar tu código TypeScript en el tiempo de ejecución de Temporal para obtener una confiabilidad extrema
This talk has been presented at TypeScript Congress 2022, check out the latest edition of this JavaScript Conference.
Aprende cómo ejecutar tu código TypeScript en el tiempo de ejecución de Temporal para obtener una confiabilidad extrema
This talk has been presented at TypeScript Congress 2022, check out the latest edition of this JavaScript Conference.
Lauren es una ingeniera de tiempo de ejecución de lenguaje que trabaja en Temporal, particularmente en el SDK de TypeScript. Ella también es autora de un libro sobre GraphQL llamado la Guía de GraphQL.
En sistemas distribuidos como el manejo de transacciones con APIs externas (por ejemplo, Stripe), se abordan problemas como la idempotencia de las operaciones para evitar acciones repetidas y la consistencia entre la base de datos y la comprensión externa del mundo, como las discrepancias entre Stripe y la base de datos local.
En caso de fallos, se implementan estrategias como la cancelación de la reserva del inventario si el cobro falla, el reembolso y la cancelación de la reserva si el envío no procede, y reintentos de operaciones transitorias fallidas usando una función de reintentos con intentos máximos y tiempo de espera.
Un token de idempotencia es un identificador único utilizado para evitar que una operación se realice más de una vez. Es crucial en transacciones para asegurar que, aunque una solicitud se envíe múltiples veces, la operación solo se ejecute una vez, evitando duplicados y efectos no deseados.
Temporal proporciona un marco de ejecución de código duradero que gestiona y persiste el estado de los flujos de trabajo, asegurando que cada paso se ejecute correctamente y permitiendo retomar procesos exactamente en el mismo punto y estado en caso de fallos, lo que elimina la necesidad de manejar manualmente la persistencia y los reintentos.
Temporal utiliza un servidor y SDKs que gestionan la persistencia de los estados de los flujos de trabajo. Los trabajadores de Temporal reportan el progreso al servidor, que guarda cada estado del flujo de trabajo, permitiendo retomar el proceso en el mismo punto y estado incluso después de fallos.
Temporal permite escribir código a un nivel de abstracción más alto, donde la persistencia y los reintentos se manejan automáticamente. Esto libera a los desarrolladores de la responsabilidad de manejar directamente estos aspectos, lo que puede reducir errores y mejorar la eficiencia en el desarrollo de software.
Así es como se construyen sistemas distribuidos en TypeScript. Trabajaremos en una tienda de comercio electrónico e implementaremos un punto de enlace para crear pedidos. Repasaremos todos los modos de fallo e intentaremos hacerlo más confiable. Reservamos el inventario, cobramos el pago y enviamos el paquete. Si alguna de estas etapas falla, la manejamos adecuadamente y consideramos reintentar.
Hola, amigos. Así es como se construyen sistemas distribuidos en TypeScript. Mi nombre es Lauren, y escribí un libro sobre GraphQL llamado la Guía de GraphQL, y actualmente trabajo como ingeniera de tiempo de ejecución de lenguaje en Temporal, trabajando en nuestro SDK de TypeScript.
Cuando trabajamos en un monolito, es posible que no tengamos muchas preocupaciones sobre sistemas distribuidos de las que preocuparnos. Si solo tenemos una capa de servidor de aplicaciones y una base de datos que realiza transacciones, es posible que solo estemos recibiendo una solicitud de un usuario, realizando una operación única y, si falla, le decimos a nuestro usuario que lo intente de nuevo. Si estamos hablando con API externas y son una parte esencial de la aplicación comercial como, por ejemplo, hablar con Stripe y decirle que cobre esto, y luego actualizar mi base de datos, entonces tengo algunos problemas más, como que es posible que no pueda comunicarme con Stripe. Así que necesito reintentarlo. Cuando reintentamos, debemos hacerlo de forma idempotente, para no cobrarle al usuario dos veces. Y hay casos en los que mi base de datos puede estar desincronizada con la comprensión de Stripe del mundo. Stripe devuelve éxito y no puedo comunicarme con la base de datos y esa operación falla o mi proceso se detiene antes de llegar a ese paso, entonces mis datos no son consistentes.
En esta charla, trabajaremos en una tienda de comercio electrónico e implementaremos un punto de enlace para crear pedidos. Y repasaremos todos los modos de fallo e intentaremos hacerlo más confiable, y no llegaremos a una confiabilidad total, pero veremos cuánto más simple podemos hacerlo si lo escribimos en temporal. Para comenzar, tenemos una función serverless alojada en Vercel, y obtenemos del cuerpo el ID del artículo, la cantidad y la dirección a la que se envía, y obtenemos el ID del usuario del frasco. Y luego tomamos tres pasos cada vez que tenemos un nuevo pedido. Reservamos el inventario con el servicio de inventario. Luego hablamos con el servicio de pagos para cobrar, y luego hablamos con el servicio de cumplimiento para enviar el paquete. Y respondemos con éxito.
Entonces, en un caso exitoso, esta lógica funciona bien, pero hay varias cosas que pueden salir mal. Si no podemos reservar, entonces no queremos continuar cobrando y enviando paquetes. Si logramos reservar y luego fallamos al cobrar, entonces no solo no queremos enviar el paquete, sino que también queremos cancelar la reserva del inventario para que otra persona pueda comprar esos artículos. Y por último, si la reserva y el cobro son exitosos y recibimos ese mismo paquete, entonces queremos reembolsar el cargo y cancelar la reserva. Ahora, cuando tenemos una falla, enviamos el estado 400 al cliente. Pero idealmente, estaríamos reintentando cada uno de estos pasos en caso de que la falla fuera transitoria, como un error de red o que el servicio estuviera temporalmente inactivo. Pero si lo intentamos nuevamente, tal vez funcione. Así que agreguemos reintentos a cada uno de estos pasos.
Tenemos una función de reintentos que maneja llamadas de servicio y las reintentamos con un retraso exponencial. Necesitamos un token de idempotencia para evitar llamadas duplicadas. También reintentamos las llamadas fallidas y capturamos los errores en el objeto de respuesta.
Tenemos una función de reintentos que toma parámetros, como intentos máximos y un tiempo de espera en intervalos. Y para cada uno de estos intentos, intenta llamar al servicio, compitiendo entre eso y el tiempo de espera. Entonces, si pasan 30 segundos y no hemos recibido respuesta del servicio, entonces reintentamos. Y cada vez que reintentamos, hacemos un retraso exponencial. Y aquí envolvemos cada una de estas llamadas de servicio en la función de reintentos.
Un problema que tenemos ahora al reintentar es que podríamos tener múltiples llamadas exitosas y necesitamos algún tipo de token de idempotencia para que el servicio sepa que no debe hacer la segunda vez. Y la mejor manera de hacer esto es obtenerlo del cliente. Así que lo agregaremos. Obtendremos un ID de solicitud del cuerpo y lo pasaremos a cada uno de los servicios.
También queremos reintentar estas llamadas fallidas, como si el reembolso falla, entonces ni reembolsaremos ni cancelaremos la reserva. Entonces el cliente terminará con el cargo y se tomará el inventario. Así que agreguemos eso. Envolveremos la cancelación de la reserva con la función de reintentos y también el reembolso. Además, cuando hablamos de lanzar, en realidad no estamos capturando el lanzamiento, solo estamos viendo si el objeto de respuesta tiene una propiedad de falla. Así que también capturaremos estos errores.
We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career
Comments