Une défaillance unique doit-elle échouer une opération en bloc?


11

Dans l'API sur laquelle je travaille, il existe une opération de suppression en masse qui accepte un tableau d'ID:

["1000", ..., "2000"]

J'étais libre d'implémenter l'opération de suppression comme je l'entendais, j'ai donc décidé de rendre le tout transactionnel: c'est-à-dire que si un seul ID n'est pas valide, la demande entière échoue. J'appellerai cela le mode strict .

try{
savepoint = conn.setSavepoint();

for(id : IDs)
    if( !deleteItem(id) ){
        conn.rollback(savepoint);
        sendHttp400AndBeDoneWithIt();
        return;
    }

conn.commit();
}

L'alternative (implémentée ailleurs dans notre suite logicielle) est de faire ce que nous pouvons dans le backend et de signaler les défaillances dans un tableau. Cette partie du logiciel traite moins de demandes, donc la réponse ne finit pas par être un tableau gigantesque ... en théorie.


Un bug récent survenu sur un serveur pauvre en ressources m'a fait revoir le code, et maintenant je remets en question ma décision initiale - mais cette fois, je suis davantage motivé par les besoins de l'entreprise que par les meilleures pratiques. Si, par exemple, j'échoue à la demande entière, l'utilisateur devra réessayer alors que si un certain nombre d'éléments sont supprimés, l'utilisateur peut terminer l'action puis demander à un administrateur de faire le reste (pendant que je travaille sur la correction du bogue !). Ce serait le mode permissif .

J'ai essayé de chercher en ligne des conseils sur la question, mais je suis venu les mains vides. Je viens donc à vous: qu'attend-on le plus des opérations en vrac de cette nature? Dois-je rester plus strict ou dois-je être plus permissif?


9
Ça dépend. Quel est le coût d'avoir quelque chose non supprimé quand il devrait l'être? (Le coût étant défini comme de mauvaises données, des maux de tête, un comportement indésirable, le temps qu'il faut à un administrateur pour le réparer, etc.) Est-ce acceptable? Si vous pouvez vivre avec les conséquences de ne pas tout manquer, allez-y. Si cela causait trop de problème, ne le faites pas. Vous connaissez votre logiciel et ses conséquences, vous devrez donc vous prononcer.
Becuzz

1
@Becuzz Le coût serait que l'utilisateur remarque un ou deux restes et ouvre un ticket à ce sujet; la situation actuelle est "omg delete is broken". Heureusement, l'utilisateur est dans le couloir, ce n'est donc pas trop un problème cette fois-ci. Le point est, j'aime faire la chose correcte à chaque fois que possible, et avec une base de code ans 10+, Dieu sait certaines choses peuvent se faire correctement à
Rath

Je pense que cela dépend aussi de savoir si vous voulez une évolutivité ou non. Si vous n'avez pas l'intention d'avoir beaucoup de pièces d'identité, cela ne devrait pas trop d'importance. Si vous avez l'intention d'avoir un million d'ID, ou mieux encore, si vous n'êtes pas absolument sûr que cela ne se produira pas, vous pouvez passer une heure à supprimer les ID juste pour le réinitialiser complètement en raison de 1 ID invalide.
imnota4

1
@ imnota4 Un excellent point que je n'avais pas considéré. L'interface utilisateur limite la demande à un maximum d'environ 250, mais le backend n'a aucune restriction. Puis-je vous demander de republier votre commentaire comme réponse?
Rath

1
Le mode permissif facilite également le travail des administrateurs car ils n'ont pas besoin de reproduire l'échec avec toute la pile des identifiants. Il pourrait également être utile d'indiquer dans la réponse la cause de chaque erreur. En regardant la cause, il pourrait être possible pour l'utilisateur final de la résoudre sans aucun ticket "omg delete is broken".
Laiv

Réponses:


9

C'est correct de faire une version «stricte» ou «sympa» d'un point de terminaison de suppression, mais vous devez clairement dire à l'utilisateur ce qui s'est passé.

Nous effectuons une action de suppression avec ce point de terminaison. Probable DELETE /resource/bulk/ou quelque chose de similaire. Je ne suis pas pointilleux. Ce qui importe ici, c'est que peu importe si vous décidez d'être strict ou gentil, vous devez rapporter exactement ce qui s'est passé.

Par exemple, une API avec laquelle je travaillais avait un DELETE /v1/student/point de terminaison qui acceptait les ID en bloc. Nous envoyions régulièrement la demande pendant les tests, obtenions une 200réponse et supposions que tout allait bien, pour découvrir plus tard que tout le monde sur la liste était toujours dans la base de données (défini sur inactif) ou pas réellement supprimé en raison d'une erreur qui foiré les appels futurs GET /v1/studentparce que nous avons récupéré des données que nous n'attendions pas.

La solution à cela est venue dans une mise à jour ultérieure qui a ajouté un corps à la réponse avec les identifiants qui n'ont pas été supprimés. Il s'agit - à ma connaissance - d'une sorte de meilleure pratique.

En fin de compte, peu importe ce que vous faites, assurez-vous de fournir un moyen de faire savoir à l'utilisateur final ce qui se passe et peut-être pourquoi cela se passe. IE, si nous choisissions un format strict, la réponse pourrait être 400 - DELETE failed on ID 1221 not found. Si nous avons choisi une version «sympa», cela pourrait l'être 207 - {message:"failed, some ids not deleted", failedids:{1221, 23432, 1224}}(excusez mon pauvre formatage json).

Bonne chance!


6
207 Multi-Statuspourrait être approprié pour cette réponse d'échec partielle
Richard Tingle

1
NOUS ALLONS! Je ne pouvais pas m'en souvenir! Je vais aller de l'avant et mettre à jour la réponse avec cela, car c'est en fait conforme à la norme.
Adam Wells

2

Il faut être strict et permissif.

Habituellement, les charges en vrac sont réparties en 2 phases:

  • Validation
  • Chargement

Au cours de la phase de validation, chaque enregistrement est examiné strictement pour s'assurer qu'il répond aux exigences des spécifications des données. On peut facilement inspecter des dizaines de milliers d'enregistrements en quelques secondes. Les enregistrements valides sont placés dans un nouveau fichier à charger, les enregistrements invalides sont marqués et supprimés et généralement placés dans un fichier séparé (sauter le fichier). Une notification est ensuite envoyée sur les enregistrements dont la validation a échoué afin qu'ils puissent être inspectés et diagnostiqués à des fins de dépannage.

Une fois les données validées, elles sont ensuite chargées. Habituellement, il est chargé par lots s'il est suffisamment volumineux pour éviter les transactions de longue durée ou s'il y a une défaillance, il sera plus facile de récupérer. La taille du lot dépend de la taille de l'ensemble de données. Si l'on n'a que quelques 1000 enregistrements, un lot serait OK. Ici, vous pouvez être un peu permissif à l'égard des échecs, mais vous pouvez définir un seuil de lot échoué pour arrêter toute l'opération. Peut-être que si [N] lots échouaient, on arrêterait toute l'opération (si le serveur était en panne ou quelque chose de similaire). Habituellement, il n'y a pas d'échecs à ce stade car les données ont déjà été validées, mais s'il y avait des problèmes d'environnement ou autres, rechargez simplement le ou les lots qui ont échoué. Cela rend la récupération un peu plus facile.


Je ne valide pas les ID par rapport aux valeurs de base de données, j'essaie simplement de les supprimer et de voir comment cela se passe, ou cela prendrait une éternité. Après l' interruption des échecs N semble une suggestion très raisonnable, 1
Rath

2

Une défaillance unique doit-elle échouer une opération en bloc?

Il n'y a pas de réponse canonique à cela. Les besoins et les conséquences pour l'utilisateur doivent être examinés et les compromis évalués. L'OP a donné certaines des informations requises, mais voici comment je procéderais:

Question 1 : "Quelle est la conséquence pour l'utilisateur si une suppression individuelle échoue?"

La réponse devrait conduire le reste du comportement de conception / implémenté.

Si, comme l'indique le type OP, c'est simplement que l'utilisateur remarque l'exception et ouvre un ticket d'incident, mais qu'il n'est pas autrement affecté (les éléments non supprimés n'affectent pas les tâches suivantes), alors j'irais avec permissive avec une notification automatique à toi.

Si les suppressions échouées doivent être résolues avant que l'utilisateur puisse continuer, alors strict est clairement préférable.

Donner à l'utilisateur l'option (par exemple, essentiellement un indicateur ignorer les échecs avec le strict ou le permissif par défaut) peut être l'approche la plus conviviale.

Question 2 : «Y aurait-il des problèmes de cohérence / cohérence des données si des tâches ultérieures étaient effectuées avec des éléments non supprimés toujours dans le magasin de données?»

Encore une fois, la réponse conduirait à la meilleure conception / comportement. Oui -> Strict, Non -> Permissif, Peut-être -> Strict ou Sélectionné par l'utilisateur (en particulier si on peut compter sur l'utilisateur pour déterminer avec précision les conséquences).


0

Je pense que cela dépend si vous voulez une évolutivité ou non. Si vous n'avez pas l'intention d'avoir beaucoup de pièces d'identité, cela ne devrait pas trop d'importance. Si vous avez l'intention d'avoir un million d'ID, ou mieux encore, si vous n'êtes pas absolument sûr que cela ne se produira pas, vous pouvez passer une heure à supprimer les ID juste pour le réinitialiser complètement en raison de 1 ID invalide.


-1

Je dirais qu'un point important ici est ce que cela signifie pour une masse de choses à supprimer.

Ces ID sont-ils en quelque sorte logiquement liés, ou s'agit-il simplement d'une commodité / performance - un regroupement par lots de ceux-ci?

En cas de connexion, même vaguement, je choisirais strict. Si c'est juste un mode batch (par exemple, l'utilisateur clique sur "enregistrer" pour ses dernières minutes de travail, et ce n'est qu'alors que le batch est transmis), alors j'irais pour la permissiveversion.

Comme l'indique l'autre réponse: dans tous les cas, dites à "l'utilisateur" exactement ce qui s'est passé.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.