En la API en la que estoy trabajando, hay una operación de eliminación masiva que acepta una matriz de ID:
["1000", ..., "2000"]
Tuve la libertad de implementar la operación de eliminación como lo creía conveniente, así que decidí hacer que todo fuera transaccional: es decir, si un solo ID no es válido, toda la solicitud falla. Llamaré a esto el modo estricto .
try{
savepoint = conn.setSavepoint();
for(id : IDs)
if( !deleteItem(id) ){
conn.rollback(savepoint);
sendHttp400AndBeDoneWithIt();
return;
}
conn.commit();
}
La alternativa (implementada en otra parte de nuestro paquete de software) es hacer lo que podamos en el backend e informar fallas en una matriz. Esa parte del software trata con menos solicitudes para que la respuesta no termine siendo una matriz gigantesca ... en teoría.
Un error reciente que ocurrió en un servidor con pocos recursos me hizo volver a mirar el código, y ahora estoy cuestionando mi decisión original, pero esta vez estoy más motivado por las necesidades comerciales que por las mejores prácticas. Si, por ejemplo, fallo la solicitud completa, el usuario tendrá que volver a intentarlo, mientras que si se eliminan varios elementos, el usuario puede finalizar la acción y luego pedirle a un administrador que haga el resto (mientras yo trabajo en solucionar el error !). Este sería el modo permisivo .
Traté de buscar orientación en línea sobre el tema, pero he venido con las manos vacías. Entonces vengo a ustedes: ¿Qué es lo que más se espera de las operaciones masivas de esta naturaleza? ¿Debo seguir estrictamente más, o debería ser más permisivo?
Respuestas:
Está bien hacer una versión 'estricta' o 'agradable' de un punto final de eliminación, pero debe decirle claramente al usuario lo que sucedió.
Estamos haciendo una acción de eliminación con este punto final. Probable
DELETE /resource/bulk/
o algo similar. Yo no soy exigente. Lo que importa aquí es que no importa si decides ser estricto o amable, debes informar exactamente lo que sucedió.Por ejemplo, una API con la que trabajé tenía un
DELETE /v1/student/
punto final que aceptaba ID masivas. Regularmente enviamos la solicitud durante las pruebas, recibimos una200
respuesta y asumimos que todo estaba bien, solo para descubrir más tarde que todos los que estaban en la lista todavía estaban EN la base de datos (configurados como inactivos) o no se eliminaron realmente debido a un error que desordenó las llamadas futurasGET /v1/student
porque recuperamos datos que no esperábamos.La solución a esto vino en una actualización posterior que agregó un cuerpo a la respuesta con los identificadores que no se eliminaron. Esto es, que yo sepa, una especie de mejor práctica.
En pocas palabras, no importa lo que hagas, asegúrate de proporcionar una manera para que el usuario final sepa qué está pasando y posiblemente por qué está sucediendo. Es decir, si elegimos un formato estricto, la respuesta podría ser
400 - DELETE failed on ID 1221 not found
. Si elegimos una versión 'agradable', podría ser207 - {message:"failed, some ids not deleted", failedids:{1221, 23432, 1224}}
(disculpe mi pobre formato json).¡Buena suerte!
fuente
207 Multi-Status
podría ser apropiado para esa respuesta parcial de fallaUno debe ser estricto y permisivo.
Por lo general, las cargas a granel se dividen en 2 fases:
Durante la fase de validación, cada registro se examina estrictamente para asegurarse de que cumple con los requisitos de las especificaciones de datos. Uno puede inspeccionar fácilmente 10s de 1000s de registros en solo unos segundos. Los registros válidos se colocan en un nuevo archivo para cargar, los inválidos se marcan y eliminan y generalmente se colocan en un archivo separado (archivo de omisión). Luego se envía una notificación en los registros que fallaron la validación para que puedan ser inspeccionados y diagnosticados con el fin de solucionar problemas.
Una vez que los datos han sido validados, se cargan. Por lo general, se carga en lotes si es lo suficientemente grande como para evitar transacciones de ejecución prolongada o si hay una falla, será más fácil de recuperar. El tamaño del lote depende de qué tan grande sea el conjunto de datos. Si uno solo tiene unos 1000 registros, un lote estaría bien. Aquí puede ser algo permisivo con las fallas, pero es posible que desee establecer un umbral de lote fallido para detener toda la operación. Tal vez si los lotes [N] fallan, uno detendría toda la operación (si el servidor estaba inactivo o algo similar). Por lo general, no hay fallas en este punto porque los datos ya se han validado, pero si se debió a problemas del entorno u otros, simplemente vuelva a cargar los lotes que fallaron. Esto hace que la recuperación sea un poco más fácil.
fuente
No hay una respuesta canónica a esto. Es necesario examinar las necesidades y las consecuencias para el usuario, y evaluar las compensaciones. El OP proporcionó parte de la información requerida, pero así es como procedería:
Pregunta 1 : "¿Cuál es la consecuencia para el usuario si falla una eliminación individual?"
La respuesta debería impulsar el resto del diseño / comportamiento implementado.
Si, como se indicó en el OP, es simplemente que el usuario nota la excepción y abre un ticket de problema, pero no se ve afectado (los elementos no eliminados no afectan las tareas posteriores), entonces iría con permisivo con una notificación automática para ti.
Si las eliminaciones fallidas deben resolverse antes de que el usuario pueda continuar, entonces estrictamente es claramente preferible.
Darle al usuario la opción (por ejemplo, esencialmente un indicador de ignorar fallas con el estricto o permisivo como predeterminado) puede ser el enfoque más fácil de usar.
Pregunta 2 : '¿Habría algún problema de coherencia / coherencia de datos si las tareas posteriores se realizan con elementos no eliminados todavía en el almacén de datos?'
Nuevamente, la respuesta conduciría al mejor diseño / comportamiento. Sí -> Estricto, No -> Permisivo, Quizás -> Estricto o Seleccionado por el usuario (particularmente si se puede confiar en el usuario para determinar con precisión las consecuencias).
fuente
Creo que esto depende de si quieres escalabilidad o no. Si no tiene la intención de tener muchas identificaciones, no debería importar demasiado. Si tiene la intención de tener un millón de ID, o mejor aún, no está absolutamente seguro de que no sucederá, entonces podría pasar una hora eliminando las ID solo para que se restablezca por completo debido a 1 ID no válida.
fuente
Diría que un punto importante aquí es lo que significa que se elimine una gran cantidad de cosas.
¿Estas identificaciones están relacionadas lógicamente de alguna manera, o es solo una conveniencia / rendimiento: agrupación por lotes de estas?
En caso de que de alguna manera, incluso sin conexión, esté conectado, iría por
strict
. Si es solo un modo por lotes (por ejemplo, el usuario hace clic en "guardar" para sus últimos minutos de trabajo, y solo entonces se transmite el lote), entonces elegiría lapermissive
versión.Como dice la otra respuesta: En cualquier caso, dígale al "usuario" exactamente lo que sucedió.
fuente