Comment contourner le manque de transactions dans MongoDB?


139

Je sais qu'il y a des questions similaires ici, mais elles me disent soit de revenir aux systèmes SGBDR réguliers si j'ai besoin de transactions, soit d'utiliser des opérations atomiques ou une validation en deux phases . La deuxième solution semble le meilleur choix. Le troisième je ne souhaite pas suivre car il semble que beaucoup de choses pourraient mal tourner et je ne peux pas le tester dans tous les aspects. J'ai du mal à refactoriser mon projet pour effectuer des opérations atomiques. Je ne sais pas si cela vient de mon point de vue limité (je n'ai travaillé qu'avec des bases de données SQL jusqu'à présent), ou si cela ne peut pas être fait.

Nous souhaitons tester MongoDB dans notre entreprise. Nous avons choisi un projet relativement simple - une passerelle SMS. Il permet à notre logiciel d'envoyer des SMS au réseau cellulaire et la passerelle fait le sale boulot: communiquer réellement avec les fournisseurs via différents protocoles de communication. La passerelle gère également la facturation des messages. Chaque client qui demande le service doit acheter des crédits. Le système diminue automatiquement le solde de l'utilisateur lorsqu'un message est envoyé et refuse l'accès si le solde est insuffisant. De plus, parce que nous sommes clients de fournisseurs de SMS tiers, nous pouvons également avoir nos propres soldes avec eux. Nous devons également en garder une trace.

J'ai commencé à réfléchir à la façon dont je pouvais stocker les données requises avec MongoDB si je réduisais une certaine complexité (facturation externe, envoi de SMS en file d'attente). Venant du monde SQL, je créerais une table séparée pour les utilisateurs, une autre pour les messages SMS et une pour stocker les transactions concernant le solde des utilisateurs. Disons que je crée des collections séparées pour toutes celles de MongoDB.

Imaginez une tâche d'envoi de SMS avec les étapes suivantes dans ce système simplifié:

  1. vérifier si l'utilisateur a un solde suffisant; refuser l'accès s'il n'y a pas assez de crédit

  2. envoyer et stocker le message dans la collection SMS avec les détails et le coût (dans le système en direct, le message aurait un statusattribut et une tâche le ramasserait pour la livraison et fixerait le prix du SMS en fonction de son état actuel)

  3. diminuer le solde des utilisateurs par le coût du message envoyé

  4. consigner la transaction dans la collection de transactions

Quel est le problème avec ça? MongoDB ne peut effectuer des mises à jour atomiques que sur un seul document. Dans le flux précédent, il peut arriver qu'une sorte d'erreur se glisse et que le message soit stocké dans la base de données, mais le solde de l'utilisateur n'est pas mis à jour et / ou la transaction n'est pas enregistrée.

J'ai eu deux idées:

  • Créez une collection unique pour les utilisateurs et stockez le solde sous forme de champ, les transactions et messages liés à l'utilisateur en tant que sous-documents dans le document de l'utilisateur. Parce que nous pouvons mettre à jour les documents de manière atomique, cela résout en fait le problème de transaction. Inconvénients: si l'utilisateur envoie de nombreux SMS, la taille du document peut devenir importante et la limite de 4 Mo de document peut être atteinte. Peut-être que je peux créer des documents d'histoire dans de tels scénarios, mais je ne pense pas que ce soit une bonne idée. De plus, je ne sais pas à quelle vitesse le système serait si je transmettais de plus en plus de données au même gros document.

  • Créez une collection pour les utilisateurs et une pour les transactions. Il peut y avoir deux types de transactions: achat à crédit avec changement de solde positif et messages envoyés avec changement de solde négatif. La transaction peut avoir un sous-document; par exemple, dans les messages envoyés, les détails du SMS peuvent être intégrés dans la transaction. Inconvénients: je ne stocke pas le solde actuel de l'utilisateur, je dois donc le calculer à chaque fois qu'un utilisateur essaie d'envoyer un message pour dire si le message peut passer ou non. Je crains que ce calcul ne devienne lent à mesure que le nombre de transactions stockées augmente.

Je ne sais pas trop quelle méthode choisir. Y a-t-il d'autres solutions? Je n'ai pas trouvé de bonnes pratiques en ligne sur la manière de contourner ce type de problèmes. Je suppose que de nombreux programmeurs qui essaient de se familiariser avec le monde NoSQL sont confrontés à des problèmes similaires au début.


61
Pardonnez-moi si je me trompe, mais il semble que ce projet utilisera un magasin de données NoSQL, qu'il en profite ou non. Les NoSQL ne sont pas une alternative à SQL en tant que choix «mode», mais pour les cas où la technologie des SGBDR relationnels ne correspond pas à l'espace du problème et une banque de données non relationnelle le fait. Une grande partie de votre question a "Si c'était SQL alors ..." et cela me fait sonner des avertissements. Tous les NoSQL sont venus d'un besoin de résoudre un problème que SQL ne pouvait pas et ensuite ils ont été quelque peu généralisés pour rendre plus facile à utiliser et alors bien sûr le train en marche commence à rouler.
PurplePilot le

4
Je suis conscient que ce projet n'est pas exactement le meilleur pour essayer NoSQL. Cependant, j'ai peur si nous commençons à l'utiliser avec d'autres projets (disons un logiciel de gestion de collection de bibliothèque parce que nous sommes dans la gestion de collection) et soudain une sorte de demande qui nécessite des transactions (et c'est en fait là, imaginez qu'un livre est transféré d'une collection à une autre), nous devons savoir comment surmonter le problème. C'est peut-être juste moi qui ai l'esprit étroit et pense qu'il y a toujours un besoin de transactions. Mais il se pourrait qu'il y ait un moyen de les surmonter d'une manière ou d'une autre.
NagyI

3
Je suis d'accord avec PurplePilot, vous devriez choisir une technologie qui correspond à une solution, pas essayer de greffer une solution qui ne convient pas à un problème. La modélisation des données pour les bases de données de graphes est un paradigme complètement différent de la conception de SGBDR et vous devez oublier tout ce que vous savez et réapprendre la nouvelle façon de penser.

9
Je comprends que je devrais utiliser l'outil approprié pour la tâche. Cependant pour moi - quand je lis des réponses comme celle-ci - il semble que NoSQL n'est pas bon pour tout ce qui est essentiel pour les données. C'est bon pour Facebook ou Twitter où si certains commentaires se perdent, le monde continue, mais tout ce qui se trouve au-dessus de cela est en panne. Si c'est vrai, je ne comprends pas pourquoi les autres se soucient de la construction, par exemple. une boutique en ligne avec MongoDB: kylebanker.com/blog/2010/04/30/mongodb-and-ecommerce Il mentionne même que la plupart des transactions peuvent être surmontées avec des opérations atomiques. Ce que je recherche, c'est le comment.
NagyI

2
Vous dites "il semble que NoSQL n'est pas bon pour tout ce qui est essentiel pour les données" n'est pas vrai là où ce n'est pas bon (peut-être) est le traitement transactionnel de type ACID. Les NoSQL sont également conçus pour les magasins de données distribués que les magasins de type SQL peuvent être très difficiles à réaliser lorsque vous entrez dans les scénarios de réplication maître-esclave. NoSQL a des stratégies pour une cohérence éventuelle et pour garantir que seul le dernier ensemble de données est utilisé, mais pas ACID.
PurplePilot le

Réponses:


23

À partir de 4.0, MongoDB aura des transactions ACID multi-documents. Le plan consiste à activer les déploiements de jeux de réplicas en premier, suivis des clusters fragmentés. Les transactions dans MongoDB se sentiront exactement comme les transactions que les développeurs connaissent à partir de bases de données relationnelles - elles seront multi-instructions, avec une sémantique et une syntaxe similaires (comme start_transactionet commit_transaction). Il est important de noter que les modifications apportées à MongoDB qui activent les transactions n'ont pas d'impact sur les performances des charges de travail qui n'en ont pas besoin.

Pour plus de détails, cliquez ici .

Avoir des transactions distribuées ne signifie pas que vous devez modéliser vos données comme dans des bases de données relationnelles tabulaires. Adoptez la puissance du modèle de document et suivez les bonnes pratiques recommandées de modélisation des données.


1
Les transactions sont arrivées! 4.0 GA'ed. mongodb.com/blog/post/…
Grigori Melnik

Les transactions MongoDB ont toujours une limitation de la taille de la transaction 16 Mo. Récemment, j'ai eu un cas d'utilisation où je dois mettre 50k enregistrements d'un fichier dans mongoDB, donc afin de maintenir la propriété atomique, j'ai pensé à utiliser des transactions, mais depuis 50k enregistrements json dépasser cette limite, il renvoie l'erreur "La taille totale de toutes les opérations de transaction doit être inférieure à 16793600. La taille réelle est 16793817". pour plus de détails, vous pouvez consulter le billet officiel jira ouvert à mongoDB jira.mongodb.org/browse/SERVER-36330
Gautam Malik

MongoDB 4.2 (actuellement en version bêta, RC4) prend en charge les transactions importantes. En représentant les transactions sur plusieurs entrées oplog, vous serez en mesure d'écrire plus de 16 Mo de données dans une seule transaction ACID (sous réserve du temps d'exécution maximum par défaut existant de 60 secondes). Vous pouvez les essayer maintenant - mongodb.com/download-center/community
Grigori Melnik

MongoDB 4.2 est maintenant GA avec un support complet des transactions distribuées. mongodb.com/blog/post/…
Grigori Melnik

83

Vivre sans transactions

Les transactions prennent en charge les propriétés ACID , mais bien qu'il n'y ait pas de transactions MongoDB, nous avons des opérations atomiques. Eh bien, les opérations atomiques signifient que lorsque vous travaillez sur un seul document, ce travail sera terminé avant que quiconque ne voie le document. Ils verront tous les changements que nous avons faits ou aucun d'entre eux. Et en utilisant des opérations atomiques, vous pouvez souvent accomplir la même chose que nous aurions accomplie en utilisant des transactions dans une base de données relationnelle. Et la raison en est que, dans une base de données relationnelle, nous devons apporter des modifications à plusieurs tables. Habituellement, des tables doivent être jointes et nous voulons donc tout faire en même temps. Et pour ce faire, comme il y a plusieurs tables, nous devrons commencer une transaction et faire toutes ces mises à jour, puis terminer la transaction. Mais avecMongoDB, nous allons intégrer les données, car nous allons les pré-joindre dans des documents et ce sont ces documents riches qui ont une hiérarchie. Nous pouvons souvent accomplir la même chose. Par exemple, dans l'exemple de blog, si nous voulions nous assurer que nous avons mis à jour un article de blog de manière atomique, nous pouvons le faire car nous pouvons mettre à jour l'intégralité de l'article de blog à la fois. Là où, comme s'il s'agissait d'un tas de tables relationnelles, nous devrons probablement ouvrir une transaction afin de pouvoir mettre à jour la collection d'articles et la collection de commentaires.

Alors, quelles sont nos approches que nous pouvons adopter MongoDBpour surmonter un manque de transactions?

  • restructurer - restructurer le code, afin que nous travaillions dans un seul document et profitions des opérations atomiques que nous proposons dans ce document. Et si nous faisons cela, nous sommes généralement tous prêts.
  • implémenter dans le logiciel - nous pouvons implémenter le verrouillage dans le logiciel, en créant une section critique. Nous pouvons créer un test, un test et un ensemble en utilisant la fonction de recherche et de modification. Nous pouvons construire des sémaphores, si nécessaire. Et d'une certaine manière, c'est ainsi que fonctionne le monde dans son ensemble. Si nous y réfléchissons, si une banque a besoin de transférer de l'argent vers une autre banque, elle ne vit pas dans le même système relationnel. Et ils ont chacun souvent leurs propres bases de données relationnelles. Et ils doivent être en mesure de coordonner cette opération même si nous ne pouvons pas commencer la transaction et terminer la transaction dans ces systèmes de base de données, mais uniquement dans un système au sein d'une banque. Les logiciels peuvent donc certainement contourner le problème.
  • tolérer - l'approche finale, qui fonctionne souvent dans les applications Web modernes et d'autres applications qui absorbent une énorme quantité de données, consiste simplement à tolérer un peu d'incohérence. Un exemple serait, si nous parlons d'un flux d'amis dans Facebook, peu importe si tout le monde voit votre mur mis à jour simultanément. Si ça va, si une personne est à quelques coups de retard pendant quelques secondes et qu'elle se rattrape. Il n'est souvent pas essentiel dans de nombreuses conceptions de système que tout soit parfaitement cohérent et que tout le monde ait une vue parfaitement cohérente et la même de la base de données. Nous pourrions donc simplement tolérer un peu d'incohérence qui est quelque peu temporaire.

Update, findAndModify, $addToSet(Dans une mise à jour) et $push(dans une mise à jour) les opérations fonctionnent atomiquement dans un seul document.


2
J'aime la façon dont cette réponse fait, au lieu de continuer à nous demander si nous devrions revenir à la DB relationnelle. Merci @xameeramir!
DonnyTian

3
une section critique de code ne fonctionnera pas si vous avez plus d'un serveur, devez utiliser un service de verrouillage distribué externe
Alexander Mills

@AlexanderMills Pouvez-vous élaborer s'il vous plaît?
Zameer

answere semble être la transcription de la vidéo d'ici: youtube.com/watch?v=_Iz5xLZr8Lw
Fritz

Je pense que cela semble bien jusqu'à ce que nous soyons limités à opérer sur une seule collecte. Mais nous ne pouvons pas tout mettre dans un seul document pour des raisons variées (taille du document ou si vous utilisez des références). Je pense alors que nous aurons peut-être besoin de transactions.
user2488286

24

Vérifiez ceci , par Tokutek. Ils développent un plugin pour Mongo qui promet non seulement des transactions mais également une augmentation des performances.


@Giovanni Bitliner. Tokutek a depuis été acquis par Percona, et sur le lien que vous avez donné, je ne vois aucune référence à des informations sur quoi que ce soit qui s'est passé depuis la publication. Savez-vous ce qui est arrivé à leur effort? J'ai envoyé l'adresse e-mail sur cette page pour le savoir.
Tyler Collier

De quoi avez-vous besoin spécifiquement? Si vous avez besoin de la technologie toku appliquée à Mongodb, essayez github.com/Tokutek/mongo , si vous avez besoin de la version mysql, ils l'ont peut-être ajoutée à leur version standard de Mysql qu'ils fournissent habituellement
Giovanni Bitliner

Comment puis-je intégrer tokutek avec nodejs.
Manoj Sanjeewa

11

Amenez-le à l' essentiel : si l'intégrité transactionnelle est indispensable, n'utilisez pas MongoDB mais utilisez uniquement les composants du système prenant en charge les transactions. Il est extrêmement difficile de construire quelque chose au-dessus du composant afin de fournir une fonctionnalité similaire à ACID pour les composants non conformes à ACID. Selon les cas d'utilisation individuels, il peut être judicieux de séparer les actions en actions transactionnelles et non transactionnelles d'une manière ou d'une autre ...


1
Je suppose que vous voulez dire que NoSQL peut être utilisé comme base de données secondaire avec un SGBDR classique. Je n'aime pas l'idée de mélanger NoSQL et SQL dans le même projet. Cela augmente la complexité et introduit éventuellement des problèmes non triviaux.
NagyI

1
Les solutions NoSQL sont rarement utilisées seules. Les magasins de documents (mongo et canapé) sont probablement la seule exception à cette règle.
Karoly Horvath

7

Quel est le problème avec ça? MongoDB ne peut effectuer des mises à jour atomiques que sur un seul document. Dans le flux précédent, il peut arriver qu'une sorte d'erreur se glisse et que le message soit stocké dans la base de données, mais le solde de l'utilisateur n'est pas réduit et / ou la transaction n'est pas enregistrée.

Ce n'est pas vraiment un problème. L'erreur que vous avez mentionnée est soit une erreur logique (bogue), soit une erreur d'E / S (réseau, panne de disque). Un tel type d'erreur peut laisser les magasins sans transaction et transactionnels dans un état non cohérent. Par exemple, s'il a déjà envoyé des SMS mais lors du stockage, une erreur s'est produite - il ne peut pas annuler l'envoi de SMS, ce qui signifie qu'il ne sera pas enregistré, le solde de l'utilisateur ne sera pas réduit, etc.

Le vrai problème ici est que l'utilisateur peut profiter des conditions de course et envoyer plus de messages que son solde ne le permet. Cela s'applique également au SGBDR, sauf si vous envoyez des SMS à l'intérieur d'une transaction avec verrouillage du champ de solde (ce qui serait un grand goulot d'étranglement). Comme solution possible pour MongoDB, il faudrait d'abord utiliser findAndModifypour réduire le solde et le vérifier, s'il est négatif, interdire l'envoi et rembourser le montant (incrément atomique). S'il est positif, continuez à envoyer et en cas d'échec, rembourser le montant. La collection d'historique de solde peut également être gérée pour aider à corriger / vérifier le champ de solde.


Merci pour cette excellente réponse! Je sais que si j'utilise des stockages compatibles avec les transactions, les données peuvent être corrompues à cause du système SMS qui ne me contrôle pas. Cependant, avec Mongo, il est possible qu'une erreur de données se produise également en interne. Disons que le code change le solde de l'utilisateur avec findAndModify, le solde devient négatif mais avant que je puisse corriger l'erreur, une erreur se produit et l'application doit redémarrer. Je suppose que vous voulez dire que je devrais implémenter quelque chose de similaire à un commit en deux phases basé sur la collecte des transactions et faire une vérification de correction régulière sur la base de données.
NagyI

9
Ce n'est pas vrai, les magasins transactionnels seront annulés si vous ne faites pas de validation finale.
Karoly Horvath

9
De plus, vous n'envoyez pas de SMS puis vous vous connectez à DB, c'est tout simplement faux. Commencez par tout stocker dans DB et effectuez une validation finale, puis vous pouvez envoyer le message. À ce stade, quelque chose peut toujours échouer, vous avez donc besoin d'un travail cron pour vérifier que le message a bien été envoyé, sinon essayez de l'envoyer. Peut-être qu'une file d'attente de messages dédiée serait meilleure pour cela. Mais tout se résume à savoir si vous pouvez envoyer des SMS de manière transactionnelle ...
Karoly Horvath

@NagyI oui, c'est ce que je voulais dire. Il faut échanger les avantages des transactions pour faciliter l'évolutivité. Fondamentalement, l'application doit s'attendre à ce que deux documents dans des collections différentes puissent être dans un état incohérent et être prêt à gérer cela. @yi_H il reviendra mais l'état ne sera plus réel (les informations sur le message seront perdues). Ce n'est pas beaucoup mieux que d'avoir simplement des données partielles (comme un solde réduit mais pas d'informations de message ou vice versa).
pingw33n

Je vois. Ce n'est en fait pas une contrainte facile. Je devrais peut-être en savoir plus sur la façon dont les systèmes SGBDR effectuent des transactions. Pouvez-vous recommander une sorte de matériel en ligne ou un livre où je peux lire à ce sujet?
NagyI

6

Le projet est simple, mais vous devez prendre en charge les transactions pour le paiement, ce qui rend le tout difficile. Ainsi, par exemple, un système de portail complexe avec des centaines de collections (forum, chat, publicités, etc.) est à certains égards plus simple, car si vous perdez une entrée de forum ou de chat, personne ne s'en soucie vraiment. Si, par contre, vous perdez une transaction de paiement, c'est un problème grave.

Donc, si vous voulez vraiment un projet pilote pour MongoDB, choisissez-en un qui soit simple à cet égard.


Merci pour l'explication. Triste d'entendre ça. J'aime la simplicité de NoSQL et l'utilisation de JSON. Nous recherchons une alternative à l'ORM mais il semble que nous devions nous y tenir pendant un moment.
NagyI

Pouvez-vous donner de bonnes raisons pour lesquelles MongoDB est meilleur que SQL pour cette tâche? Le projet pilote semble un peu idiot.
Karoly Horvath le

Je n'ai pas dit que MongoDB est meilleur que SQL. Nous voulons simplement savoir si c'est mieux que SQL + ORM. Mais maintenant, il devient plus clair qu'ils ne sont pas compétitifs dans ce type de projets.
NagyI

6

Les transactions sont absentes de MongoDB pour des raisons valables. C'est l'une de ces choses qui accélèrent MongoDB.

Dans votre cas, si la transaction est un must, mongo ne semble pas un bon choix.

Peut-être RDMBS + MongoDB, mais cela ajoutera des complexités et rendra plus difficile la gestion et le support de l'application.


1
Il existe maintenant une distribution de MongoDB appelée TokuMX qui utilise la technologie fractale pour offrir une amélioration des performances 50x et offre une prise en charge complète des transactions ACID en même temps: tokutek.com/tokumx-for-mongodb
OCDev

9
Comment une transaction pourrait-elle ne pas être un "must". Dès que vous avez besoin d'un cas simple où vous devez mettre à jour 2 tables, mongo n'est soudainement plus un bon choix? Cela ne laisse pas du tout beaucoup de cas d'utilisation.
Mr_E

1
@Mr_E d'accord, c'est pourquoi MongoDB est un peu stupide :)
Alexander Mills

6

C'est probablement le meilleur blog que j'ai trouvé concernant l'implémentation d'une fonctionnalité de type transaction pour mongodb.!

Indicateur de synchronisation: idéal pour copier simplement des données à partir d'un document maître

Job Queue: à usage très général, résout 95% des cas. La plupart des systèmes doivent de toute façon avoir au moins une file d'attente de travaux!

Validation en deux phases: cette technique garantit que chaque entité dispose toujours de toutes les informations nécessaires pour atteindre un état cohérent

Log Reconciliation: la technique la plus robuste, idéale pour les systèmes financiers

Gestion des versions: assure l'isolation et prend en charge les structures complexes

Lisez ceci pour plus d'informations: https://dzone.com/articles/how-implement-robust-and


Veuillez inclure les parties pertinentes de la ressource liée nécessaires pour répondre à la question dans votre réponse. En l'état, votre réponse est très sensible à la pourriture des liens (c'est-à-dire si le site Web lié tombe en panne ou change votre réponse est potentiellement inutile).
mech

Merci @mech pour la suggestion
Vaibhav

4

C'est tard, mais je pense que cela aidera à l'avenir. J'utilise Redis pour créer une file d' attente afin de résoudre ce problème.

  • Condition: L'
    image ci-dessous montre que 2 actions doivent être exécutées simultanément, mais les phases 2 et 3 de l'action 1 doivent être terminées avant de démarrer la phase 2 de l'action 2 ou inversement (une phase peut être une requête REST api, une requête de base de données ou exécuter du code javascript ... ). entrez la description de l'image ici

  • Comment une file d'attente vous aide
    Queue assurez-vous que chaque code de bloc entre lock()et release()dans plusieurs fonctions ne s'exécutera pas en même temps, faites-les isoler.

    function action1() {
      phase1();
      queue.lock("action_domain");
      phase2();
      phase3();
      queue.release("action_domain");
    }
    
    function action2() {
      phase1();
      queue.lock("action_domain");
      phase2();
      queue.release("action_domain");
    }
  • Comment créer une file d'attente
    Je me concentrerai uniquement sur la façon d'éviter la partie de condition de course lors de la création d'une file d'attente sur le site backend. Si vous ne connaissez pas l'idée de base de la file d'attente, venez ici .
    Le code ci-dessous ne montre que le concept, vous devez l'implémenter de manière correcte.

    function lock() {
      if(isRunning()) {
        addIsolateCodeToQueue(); //use callback, delegate, function pointer... depend on your language
      } else {
        setStateToRunning();
        pickOneAndExecute();
      }
    }
    
    function release() {
      setStateToRelease();
      pickOneAndExecute();
    }

Mais vous devez vous isRunning() setStateToRelease() setStateToRunning()isoler ou bien vous affrontez à nouveau la condition de la race. Pour ce faire, j'ai choisi Redis à des fins ACID et évolutif.
Le document Redis parle de sa transaction:

Toutes les commandes d'une transaction sont sérialisées et exécutées séquentiellement. Il ne peut jamais arriver qu'une demande émise par un autre client soit servie au milieu de l'exécution d'une transaction Redis. Cela garantit que les commandes sont exécutées en une seule opération isolée.

P / s:
J'utilise Redis parce que mon service l'utilise déjà, vous pouvez utiliser n'importe quel autre moyen d'isolation de support pour le faire.
Le action_domaindans mon code est ci-dessus lorsque vous n'avez besoin que de l'appel de l'action 1 par l'utilisateur A bloquer l'action 2 de l'utilisateur A, ne pas bloquer un autre utilisateur. L'idée est de mettre une clé unique pour la serrure de chaque utilisateur.


Vous auriez reçu plus de votes positifs si votre score avait déjà été plus élevé. C'est ce que pensent la plupart des gens ici. Votre réponse est utile dans le contexte de la question. Je vous ai voté.
Mukus

3

Les transactions sont désormais disponibles dans MongoDB 4.0. Échantillon ici

// Runs the txnFunc and retries if TransientTransactionError encountered

function runTransactionWithRetry(txnFunc, session) {
    while (true) {
        try {
            txnFunc(session);  // performs transaction
            break;
        } catch (error) {
            // If transient error, retry the whole transaction
            if ( error.hasOwnProperty("errorLabels") && error.errorLabels.includes("TransientTransactionError")  ) {
                print("TransientTransactionError, retrying transaction ...");
                continue;
            } else {
                throw error;
            }
        }
    }
}

// Retries commit if UnknownTransactionCommitResult encountered

function commitWithRetry(session) {
    while (true) {
        try {
            session.commitTransaction(); // Uses write concern set at transaction start.
            print("Transaction committed.");
            break;
        } catch (error) {
            // Can retry commit
            if (error.hasOwnProperty("errorLabels") && error.errorLabels.includes("UnknownTransactionCommitResult") ) {
                print("UnknownTransactionCommitResult, retrying commit operation ...");
                continue;
            } else {
                print("Error during commit ...");
                throw error;
            }
       }
    }
}

// Updates two collections in a transactions

function updateEmployeeInfo(session) {
    employeesCollection = session.getDatabase("hr").employees;
    eventsCollection = session.getDatabase("reporting").events;

    session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );

    try{
        employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } );
        eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } );
    } catch (error) {
        print("Caught exception during transaction, aborting.");
        session.abortTransaction();
        throw error;
    }

    commitWithRetry(session);
}

// Start a session.
session = db.getMongo().startSession( { mode: "primary" } );

try{
   runTransactionWithRetry(updateEmployeeInfo, session);
} catch (error) {
   // Do something with error
} finally {
   session.endSession();
}
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.