Question

Je dois effectuer des transactions (début, validation ou annulation), des verrous (sélectionner pour la mise à jour). Comment puis-je le faire dans une base de données modèle de document?

Modifier:

Le cas est le suivant:

  • Je souhaite gérer un site de vente aux enchères.
  • Et je pense aussi à l'achat direct.
  • Lors d'un achat direct, je dois décrémenter le champ quantité dans l'enregistrement du poste, mais uniquement si la quantité est supérieure à zéro. C'est pourquoi j'ai besoin de verrous et de transactions.
  • Je ne sais pas comment résoudre ce problème sans verrou et / ou transaction.

Puis-je résoudre ce problème avec CouchDB?

Était-ce utile?

La solution

Non. CouchDB utilise une "concurrence concurrente optimiste". modèle. En termes simples, cela signifie simplement que vous envoyez une version du document avec votre mise à jour et que CouchDB refuse la modification si la version actuelle du document ne correspond pas à celle que vous avez envoyée.

C'est d'une simplicité trompeuse, vraiment. Vous pouvez recadrer de nombreux scénarios basés sur les transactions normales pour CouchDB. Cependant, vous devez en quelque sorte jeter vos connaissances de domaine SGBDR lors de l’apprentissage de CouchDB. Il est utile de traiter les problèmes d'un niveau supérieur, plutôt que d'essayer de transformer Couch dans un monde basé sur SQL.

Suivi de l'inventaire

Le problème que vous avez décrit est principalement un problème d'inventaire. Si vous disposez d'un document décrivant un élément et comprenant un champ "Quantité disponible", vous pouvez gérer les problèmes de simultanéité de la manière suivante:

  1. Récupérez le document, prenez note de la propriété _rev que CouchDB envoie avec
  2. Décrémentez le champ de quantité s'il est supérieur à zéro
  3. Renvoyez le document mis à jour à l'aide de la propriété _rev
  4. Si le _rev correspond au numéro actuellement stocké, cela sera fait!
  5. En cas de conflit (lorsque _rev ne correspond pas), récupérez la version la plus récente du document

Dans ce cas, deux scénarios de défaillance possibles doivent être envisagés. Si la version de document la plus récente comporte une quantité de 0, vous la gérez comme dans un SGBDR et avertissez l'utilisateur qu'il ne peut pas acheter ce qu'il voulait acheter. Si la version la plus récente du document a une quantité supérieure à 0, il vous suffit de répéter l'opération avec les données mises à jour et de recommencer au début. Cela vous oblige à faire un peu plus de travail qu'un SGBDR, et pourrait devenir un peu gênant s'il y a des mises à jour fréquentes et conflictuelles.

Maintenant, la réponse que je viens de donner présuppose que vous ferez les choses dans CouchDB de la même manière que dans un SGBDR. Je pourrais aborder ce problème un peu différemment:

Je commencerais par un "produit principal". document contenant toutes les données du descripteur (nom, image, description, prix, etc.). Ensuite, j'ajouterais un "ticket d'inventaire". document pour chaque instance spécifique, avec des champs pour clé_produit et réclamé_by . Si vous vendez un modèle de marteau et que vous en avez 20 à vendre, vous pouvez disposer de documents contenant des clés telles que marteau-1 , marteau-2 , etc. représente chaque marteau disponible.

Ensuite, je créerais une vue qui me donnerait une liste des marteaux disponibles, avec une fonction de réduction qui me permettrait de voir un "total". Celles-ci sont complètement improvisées, mais devraient vous donner une idée de ce à quoi une vue de travail ressemblerait.

Carte

function(doc) 
{ 
    if (doc.type == 'inventory_ticket' && doc.claimed_by == null ) { 
        emit(doc.product_key, { 'inventory_ticket' :doc.id, '_rev' : doc._rev }); 
    } 
}

Ceci me donne une liste des "billets" disponibles, par clé de produit. Je pourrais en saisir un groupe lorsque quelqu'un voudrait acheter un marteau, puis effectuer une itération en envoyant des mises à jour (à l'aide de id et de _rev ) jusqu'à ce que j'en revendique avec succès (billets précédemment revendiqués entraînera une erreur de mise à jour).

Réduire

function (keys, values, combine) {
    return values.length;
}

Cette fonction de réduction renvoie simplement le nombre total d'éléments Inventory_ticket non réclamés, afin que vous puissiez indiquer le nombre de & martins & marteaux " sont disponibles à l'achat.

Mises en garde

Cette solution représente environ 3,5 minutes de réflexion totale sur le problème particulier que vous avez présenté. Il y a peut-être de meilleures façons de le faire! Cela dit, cela réduit considérablement les mises à jour conflictuelles et la nécessité de réagir à un conflit par une nouvelle mise à jour. Dans ce modèle, plusieurs utilisateurs ne tenteront pas de modifier les données dans l'entrée du produit principal. Dans le pire des cas, vous aurez plusieurs utilisateurs qui tenteront de réclamer un seul ticket, et si vous en avez récupéré plusieurs

Autres conseils

Développer la réponse de Mr Kurt. Dans de nombreux scénarios, il n'est pas nécessaire que les billets de stock soient échangés dans l'ordre. Au lieu de sélectionner le premier ticket, vous pouvez sélectionner au hasard parmi les tickets restants. Avec un grand nombre de tickets et un grand nombre de requêtes simultanées, vous obtiendrez beaucoup moins de conflits sur ces tickets, par rapport à tous ceux qui essaient d'obtenir le premier ticket.

Un modèle de conception pour les transactions restfull consiste à créer une "tension". dans le système. Pour l'exemple de cas courant d'une transaction sur un compte bancaire, vous devez vous assurer de mettre à jour le total pour les deux comptes impliqués:

  • Créer un document de transaction "transférer 10 USD du compte 11223 vers le compte 88733". Cela crée une tension dans le système.
  • Pour résoudre toute analyse de tension pour tous les documents de transaction et
    • Si le compte source n'est pas encore mis à jour, mettez à jour le compte source (-10 USD)
    • Si le compte source a été mis à jour mais que le document de transaction ne l'indique pas, mettez-le à jour (par exemple, définissez l'indicateur "sourcedone" dans le document)
    • Si le compte cible n'est pas encore mis à jour, mettez à jour le compte cible (+10 USD)
    • Si le compte cible a été mis à jour mais que le document de transaction ne l'indique pas, mettez à jour le document de transaction
    • Si les deux comptes ont été mis à jour, vous pouvez supprimer le document de transaction ou le conserver pour vérification.

L'analyse de la tension doit être effectuée dans un processus dorsal pour tous les "documents de tension". garder les temps de tension dans le système à court. Dans l'exemple ci-dessus, il y aura une incohérence anticipée de courte durée lorsque le premier compte a été mis à jour mais que le second n'est pas encore à jour. Cela doit être pris en compte de la même manière que si votre Couchdb est distribuée avec une éventuelle cohérence.

Une autre implémentation possible évite complètement le besoin de transactions: stockez simplement les documents de tension et évaluez l’état de votre système en évaluant chaque document de tension impliqué. Dans l'exemple ci-dessus, cela signifie que le total d'un compte est uniquement déterminé comme la somme des valeurs dans les documents de transaction auxquels ce compte est associé. Dans Couchdb, vous pouvez très bien modéliser ceci sous forme de vue carte / réduction.

Non, CouchDB n'est généralement pas adapté aux applications transactionnelles car il ne prend pas en charge les opérations atomiques dans un environnement en cluster / répliqué.

CouchDB a sacrifié la capacité transactionnelle au profit de l’évolutivité. Pour avoir des opérations atomiques, vous avez besoin d’un système de coordination central, ce qui limite votre évolutivité.

Si vous pouvez garantir que vous n’avez qu’une instance de CouchDB ou que toute personne modifiant un document en particulier se connecte à la même instance de CouchDB, vous pouvez utiliser le système de détection de conflit pour créer une sorte d’atomicité à l’aide des méthodes décrites ci-dessus, mais si vous augmentez ultérieurement en cluster ou en utilisant un service hébergé tel que Cloudant, il tombera en panne et vous devrez refaire cette partie du système.

Donc, ma suggestion serait d'utiliser autre chose que CouchDB pour le solde de votre compte, ce sera beaucoup plus facile de cette façon.

En réponse au problème du PO, Couch n’est probablement pas le meilleur choix ici. L'utilisation de vues est un excellent moyen de suivre l'inventaire, mais il est plus ou moins impossible de le bloquer à 0. Le problème étant la condition de concurrence critique lorsque vous lisez le résultat d'une vue, décidez que vous pouvez utiliser un "marteau-1". élément, puis écrivez un document pour l'utiliser. Le problème est qu’il n’existe aucun moyen atomique d’écrire dans le doc pour utiliser le marteau si le résultat de la vue est qu’il y a > 0 marteau-1. Si 100 utilisateurs interrogent tous la vue en même temps et voient 1 marteau-1, ils peuvent tous écrire un document utilisant un marteau 1, ce qui donne -99 marteau-1. En pratique, la situation de concurrence critique sera relativement petite - vraiment petite si votre base de données exécute localhost. Mais une fois que vous avez évolué et que vous avez un serveur de base de données ou un cluster hors site, le problème devient beaucoup plus perceptible. Quoi qu’il en soit, il est inacceptable d’avoir une telle situation dans un système critique lié à l’argent.

Mise à jour de la réponse de M. Kurt (il se peut qu'il soit simplement daté ou qu'il ne soit peut-être pas au courant de certaines fonctionnalités de CouchDB)

Une vue est un bon moyen de gérer des opérations telles que les soldes / inventaires dans CouchDB.

Vous n'avez pas besoin d'émettre le docid et rev dans une vue. Vous obtenez ces deux éléments gratuitement lorsque vous récupérez les résultats de la vue. Les émettre, en particulier dans un format commenté comme un dictionnaire, augmentera inutilement la taille de votre vue.

Une vue simple permettant de suivre les soldes d’inventaire devrait ressembler davantage à ceci (même pour mémoire)

function( doc )
{
    if( doc.InventoryChange != undefined ) {
        for( product_key in doc.InventoryChange ) {
            emit( product_key, 1 );
        }
    }
}

Et la fonction de réduction est encore plus simple

_sum

Ceci utilise une fonction de réduction intégrée qui additionne simplement les valeurs de toutes les lignes avec touches correspondantes.

Dans cette vue, tout document peut avoir un membre "InventoryChange". qui mappe product_key à un changement dans l'inventaire total d'entre eux. c'est à dire.

{
    "_id": "abc123",
    "InventoryChange": {
         "hammer_1234": 10,
         "saw_4321": 25
     }
}

Ajouterait 10 hammer_1234 et 25 saw_4321.

{
    "_id": "def456",
    "InventoryChange": {
        "hammer_1234": -5
    }
}

Brûlerait 5 marteaux de l'inventaire.

Avec ce modèle, vous ne mettez jamais à jour de données, vous ne faites que les ajouter. Cela signifie qu'il n'y a aucune possibilité de conflit de mise à jour. Tous les problèmes transactionnels de mise à jour des données disparaissent:)

Une autre bonne chose à propos de ce modèle est que TOUS les documents de la base de données peuvent à la fois ajouter et soustraire des objets de l’inventaire. Ces documents peuvent contenir toutes sortes d'autres données. Vous pourriez avoir un "Envoi". documenter avec un tas de données sur la date et l'heure reçues, entrepôt, employé destinataire, etc. et tant que ce document définit un InventoryChange, il mettra à jour l'inventaire. Comme pourrait un "Vente" doc et un "DamagedItem" doc etc. En regardant chaque document, ils lisent très clairement. Et la vue gère tout le travail difficile.

En fait, vous pouvez en quelque sorte. Consultez la API de document HTTP et faites défiler jusqu'à l'en-tête " Modifier plusieurs documents avec une seule demande ".

En principe, vous pouvez créer / mettre à jour / supprimer un groupe de documents dans une seule demande de publication adressée à URI / {nombase} / _vrac_docs et ils réussiront tous ou échoueront. Le document met en garde que ce comportement peut changer dans le futur, cependant.

EDIT: Comme prévu, à partir de la version 0.9, la documentation en bloc ne fonctionne plus de cette façon.

Utilisez simplement le type de solution légère SQlite pour les transactions. Lorsque la transaction est terminée, répliquez-la et marquez-la comme répliquée dans SQLite

Table SQLite

txn_id    , txn_attribute1, txn_attribute2,......,txn_status
dhwdhwu$sg1   x                    y               added/replicated

Vous pouvez également supprimer les transactions répliquées avec succès.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top