2017-08-02 3 views
1

Quelle est la meilleure politique de nouvelle tentative dans un tel scénario:meilleure politique de nouvelle tentative sur délai de réponse de création de données

Database réussit à créer l'entrée de données, mais la réponse prend trop de temps pour atteindre Application. Donc, pour effectuer le travail, Application réessaie la création, et bien sûr Database renvoie une erreur "existe déjà". Donc, à la fin du point de vue de Application, il semble que la création a échoué, alors qu'en fait, il a réussi. Et pire encore, si c'est au milieu d'une série d'étapes, alors il n'y a aucun moyen pour Application de décider s'il faut déclencher une restauration sur les étapes précédentes.

L'augmentation de la longueur du délai sur Application n'est pas une solution acceptable car le réseau IP ne peut jamais être fiable à 100% et il y a toujours une petite chance que la réponse puisse se perdre dans le réseau.

Ajouter une vérification de l'existence de <data> avant de créer pourrait fonctionner. Mais c'est seulement quand la concurrence est prise en compte. Dans mon cas, il peut y avoir plusieurs clients à Database et je ne suis pas certain de la chance de conditions de course.

+-------------+            +-----------+  
| Application |            | Database |  
+-------------+            +-----------+  
     |               |   
     | CREATE <data>           |   
     |--------------------------------------------------------->|   
     |               |   
     |               | creating 
     |               |--------- 
     |               |  | 
     |               |<-------- 
     | -------------------------------\       |   
     |-| timeout waiting for response |       |   
     | |------------------------------|       |   
     |               |   
     |             SUCCESS |   
     |<---------------------------------------------------------|   
     | -----------------------------------------------\   |   
     |-| response from a timed out session is ignored |   |   
     | |----------------------------------------------|   |   
     |               |   
     | retry CREATE <data>          |   
     |--------------------------------------------------------->|   
     |               |   
     |        ERROR: <data> ALREADY EXISTS |   
     |<---------------------------------------------------------|   
     | ---------------------------------------------------\  |   
     |-| no idea whether the creation actually took place |  |   
     | |--------------------------------------------------|  |   
     |               |   
+0

Votre application n'obtient-elle pas une erreur de "connexion réseau expirée"? –

+0

@NevilleK oui c'est le cas. Et c'est ce qui déclenche la nouvelle tentative. – Sah

Répondre

0

La plupart des bases de données modernes offrent une certaine façon d'écrire « upsert » déclarations qui insérer des données atomiquement si elle n'existe pas, et mettre à jour (ou ne rien faire) si elle existe déjà. De cette façon, votre application peut réessayer en toute sécurité et n'obtiendra pas d'erreur si les données ont déjà été créées, rendant votre création de données idempotent.

Exemples pour certaines bases de données populaires:

  • MySQL:

    -- Do nothing if data exists 
    INSERT IGNORE ... 
    -- Update if data exists 
    INSERT ... ON DUPLICATE KEY UPDATE ... 
    
  • PostgreSQL:

    -- Do nothing if data exists 
    INSERT ... ON CONFLICT DO NOTHING 
    -- Update if data exists 
    INSERT ... ON CONFLICT ... DO NOTHING 
    
  • Oracle:

    -- Do nothing if data exists 
    MERGE INTO ... USING ... 
    WHEN NOT MATCHED THEN INSERT ... 
    -- Update if data exists 
    MERGE INTO ... USING ... 
    WHEN NOT MATCHED THEN INSERT ... 
    WHEN MATCHED THEN UPDATE ... 
    

Si les opérations atomiques ou les transactions ne sont pas une option, vous pouvez écrire vos opérations de base de données pour que les tentatives ne sont pas nocifs, et faire chaque opération dans une boucle qui vérifie d'abord si la base de données est déjà dans l'état souhaité, puis tente l'opération si ce n'est pas le cas, et réessaye en cas d'échec. En d'autres termes, quelque chose comme (pseudo-code):

max_retries = n 
retries = 0 
WHILE NOT database_in_desired_state 
    IF retries < max_retries THEN 
     perform_database_operation 
     retries = retries + 1 
    ELSE 
     fail 

Vous pouvez faire relances sans danger en faisant des opérations conditionnelles (par exemple UPDATE some_table SET field = value, version = version + 1 WHERE version = expected_version ou en ajoutant des contraintes uniques et similaires afin que les opérations en double ne sont pas autorisés Si vous fournir plus de détails. Sur la base de données que vous utilisez, je pourrais vous donner des conseils plus concrets:

Si vous effectuez une longue série d'opérations sur plusieurs systèmes distants, l'ensemble de l'opération doit être annulé en cas de panne et il n'y a aucun moyen d'encapsuler toutes les interactions dans une seule transaction (distribuée), vous aurez besoin d'écrire des transactions de compensation qui ramèneront manuellement le k fait jusqu'à présent sur les erreurs. Bien sûr, la transaction de compensation pourrait également échouer, et vous devez considérer comment cela devrait être géré.Une façon consiste à avoir des tâches de nettoyage périodiques qui recherchent des transactions échouées ou des états incohérents.

+0

La base de données que nous utilisons est basée sur LDAP. Et la plupart du temps une demande de client doit être remplie avec plusieurs opérations LDAP, mélangées avec certaines interactions avec un autre module parmi le processus. – Sah

+1

Merci de rendre l'exigence LDAP explicite - la plupart des gens pensent que le SGBDR lorsque vous dites "base de données", vous obtiendrez beaucoup de mauvaises réponses. –

+0

J'essayais de rendre la question plus générique parce que, à mon avis, ce n'est pas un problème qui peut être résolu par la base de données, mais plutôt un choix de politique du côté de l'application. Mais je prendrai votre avis en considération la prochaine fois que je poserai une question. – Sah

0

Tout dépend du contexte. Oui, les connexions réseau peuvent échouer - mais vous devez décider de l'ampleur du risque. Si vous utilisez une installation d'hébergement professionnel, avec un équipement de qualité professionnelle, cela arrivera - enfin, presque jamais. Dans ce cas, je ne construirais pas beaucoup de logique supplémentaire dans l'application pour gérer les problèmes de réseau; vous devez vous fier aux fonctions de gestion des transactions de votre base de données pour vous assurer que les données sont dans un état cohérent. Une fois que votre application a intercepté l'exception réseau, vous pouvez afficher une erreur à l'utilisateur et lui demander de recommencer.

Si votre environnement est intrinsèquement non fiable - vous vous connectez via l'Internet public, par exemple - le modèle d'architecture commun est d'utiliser un bus de messages, plutôt que des opérations synchrones.

L'écriture de code synchrone pour gérer des conditions réseau non fiables n'est pas triviale; vous commenceriez avec le pseudo code @markusk posté, mais j'ajouterais à cela la fermeture et la réouverture de la connexion à la base de données.