¿Cuáles son las mejores prácticas para manejar errores en acciones de varios pasos entre el cliente y los servidores?

Aug 18 2020

Tengo un sitio web que sube ofertas de trabajo a mi API, hay varios pasos para hacerlo:

  1. Cargue una imagen de logotipo en el almacenamiento de archivos.

  2. Inserte datos sobre la oferta de trabajo en una base de datos.

  3. Procesar un pago con un proveedor externo.

  4. Envíe un correo electrónico a través de un proveedor externo.

En general, puede imaginar que otros pasos están presentes aquí en diferentes aplicaciones, por ejemplo, obtener información de una API de terceros, validar un ReCAPTCHA, actualizar la API de indexación de Google, enviar un SMS, etc., etc.

Dado que todos estos utilizan terceros y son independientes del servidor que maneja la llamada API, cualquiera de ellos puede fallar al dejar algunos de los pasos completados con éxito y otros no (por ejemplo, logotipo actualizado pero pago no cobrado).

Mi pregunta es ¿cómo se manejan típicamente los errores en este tipo de acciones de varios pasos entre clientes y servidores en los sistemas de producción? ¿Existen estándares aceptados o mejores prácticas?


He considerado:

  1. No manejar errores y solo esperar que todo pase sin errores.

  2. Definir una función 'deshacer' en el backend para cada uno de los pasos y, si uno de los pasos falla, llamar a eso en los pasos anteriores. Con acciones que consisten en muchos pasos, esto podría convertirse en un código de espagueti con bastante rapidez, y algunos pasos no se pueden deshacer tan fácilmente, por ejemplo, enviar un correo electrónico.

  3. Crear un punto final separado en el backend para cada uno de los pasos y dejar que el cliente llame a cada uno por turno. Esto también podría usar un punto final de API 'deshacer', de modo que si el cliente recibe un error en uno de los pasos, puede deshacer todos los pasos anteriores. Esto tiene la ventaja de permitir que el cliente calcule el progreso de la acción que se está completando, es decir, podría mostrar '1 de 5 pasos completados' al usuario.

  4. Crear una fila en una base de datos (¿o base de datos en memoria?) para cada acción y cuando se complete cada paso, marcar la columna correspondiente como completada. Cuando se completan todas las columnas de la fila, se envía una respuesta al usuario.

Respuestas

1 RalfKleberhoff Aug 19 2020 at 13:30

Este proceso de varios pasos es una transacción, que pasa de un estado inicial consistente ("no pasó nada") a un estado final ("publicación de trabajo finalizada"), con pasos intermedios que dejan su aplicación en un estado (posiblemente) inconsistente.

No debe delegar el manejo de la transacción a su cliente, especialmente no dividir el proceso en llamadas individuales (o tal vez se salte el paso del pago, lo que es malo para el negocio).

Si falla un paso, seguramente no tiene sentido continuar, debe hacer algún tipo de reversión a un estado aceptable, no necesariamente al estado inicial. Por ejemplo, no veo una necesidad absoluta de eliminar la imagen cargada del almacenamiento de archivos.

En primer lugar, trataría de organizar los pasos de tal manera que la mayoría de los pasos intermedios sean aceptables, por lo que no hay necesidad de retroceder.

Los pasos complicados son seguramente el pago y el correo electrónico (si entiendo su negocio correctamente).

  • Facturar al cliente sin que se le envíen los correos electrónicos es malo (casi un fraude).
  • Enviar los correos electrónicos sin recibir el pago significa perder dinero (si esto no sucede con frecuencia, podría ser tolerable). Definitivamente no puede revertir los correos electrónicos una vez que se han enviado, pero tal vez pueda revertir los pagos (a menos que pierda la conexión con el proveedor en ese momento).

Como confía en las conexiones externas, no veo una manera de evitar por completo la finalización parcial de su transacción, por lo que diseñaría el proceso de tal manera que las fallas intermedias

  • muy improbable
  • o dejar un estado tolerable.

Entonces, yo

  1. Haga ping a todos los servicios externos para asegurarse de que estén actualmente activos y accesibles
  2. Cargue una imagen de logotipo en el almacenamiento de archivos.
  3. Inserte datos sobre la oferta de trabajo en una base de datos.
  4. Envíe un correo electrónico a través de un proveedor externo.
  5. Procesar un pago con un proveedor externo.

El procedimiento de reversión sería

  • eliminar la imagen del almacenamiento de archivos,
  • eliminar el anuncio de trabajo de la base de datos,
  • si se envió un correo electrónico, pero el pago falló: programe la solicitud de pago para un reintento posterior.
JacquesB Aug 18 2020 at 21:56

"Mejor" obviamente depende de los requisitos. El número 1 es claramente el más simple de implementar, pero las transacciones se perderán o quedarán incompletas en caso de errores. ¿Quizás esta es una compensación aceptable desde una perspectiva comercial?

La solución más robusta es dividir el proceso en una serie de pasos donde cada paso es una transacción. Una transacción se completa o falla, y si falla, se puede volver a intentar de manera segura. (Por ejemplo, enviar un correo o un sms sería una transacción). Una fila de la base de datos realiza un seguimiento de los pasos que se han completado.

No creo que debas hacer que el cliente llame a cada paso. Eso crearía un acoplamiento estrecho y solo aumentaría la complejidad. Simplemente haga que el cliente llame a una sola solicitud con todos los datos necesarios, lo que inicia el flujo de trabajo. Luego, el cliente puede enviar una solicitud periódica por separado para sondear el estado si desea mostrar el progreso.

El soporte para deshacer es más complicado y (como observará) no siempre es posible. Si algún paso puede fallar de una manera en la que se debe rechazar todo el proceso (como si una tarjeta de crédito no fuera válida), creo que debería realizarse en un paso de validación antes de que se inicie el proceso de varios pasos. Esto le permitirá dar retroalimentación sincrónica al cliente y hacer que el cliente revise y vuelva a ingresar los datos.

AdrianL Aug 27 2020 at 01:00

Como mencionaron otros, idealmente desea agrupar todas las piezas necesarias para una publicación de trabajo exitosa y tener un cliente para enviarlo a su backend en una sola solicitud. Como desarrollador móvil, siempre abogo por que los clientes hagan la menor cantidad de trabajo posible para preservar el uso de datos y la duración de la batería.

En cuanto al backend, sugeriría intentar insertar primero la información más importante en la base de datos y luego intentar, por ejemplo, insertar datos complementarios después, como una imagen de logotipo.