Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose.js transactions

I know MongoDB doesn't support transactions as relational databases do, but I still wonder how to achieve atomicity for several operations. Hunting around the web, I see people mentioning Transactions without Transactions. Reading through the slides, I am still not clear how to implement that with Mongoose.js.

Take this code snippet for example:

player.save(callback1);
story.save(callback2);

How do I implement callback1 and callback2 so that they either succeed together or fail together?

like image 828
Zebra Propulsion Lab Avatar asked Jul 03 '13 22:07

Zebra Propulsion Lab


People also ask

What are transactions in Mongoose?

Transactions in Mongoose. Transactions are new in MongoDB 4.0 and Mongoose 5.2. 0. Transactions let you execute multiple operations in isolation and potentially undo all the operations if one of them fails.

Can MongoDB do transactions?

For situations that require atomicity of reads and writes to multiple documents (in a single or multiple collections), MongoDB supports multi-document transactions. With distributed transactions, transactions can be used across multiple operations, collections, databases, documents, and shards.

What is transactions in node JS?

A transaction is a unit of work, composed of a series of operations that you want either to succeed together, or fail together when one or more of the operations fail. This behavior is called atomicity.

What problem does Mongoose JS solve?

The problem that Mongoose aims to solve is allowing developers to enforce a specific schema at the application layer. In addition to enforcing a schema, Mongoose also offers a variety of hooks, model validation, and other features aimed at making it easier to work with MongoDB.


2 Answers

If you really must have transactions across multiple documents types (in separate collections) the means to achieve this is with a single table that stores actions to take.

db.actions.insert( { actions: [{collection: 'players', _id: 'p1', update: {$set : {name : 'bob'} } },             {collection: 'stories', _id: 's1', update: {$set : {location: 'library'} } }], completed: false }, callback); 

This insert is atomic, and all done at once. You then can perform the commands in the 'actions' collection and mark them as complete or delete them as you complete them, calling your original callback when they are all completed. This only works if your actions processing loop is the only thing updating the db. Of course you'll have to stop using mongoose, but the sooner you do that the better you'll be anyway.

like image 119
Will Shaver Avatar answered Sep 19 '22 12:09

Will Shaver


This question is quite old but for anyone who stumbles upon this page, you could use fawn. It's an npm package that solves this exact problem. Disclosure: I wrote it

Say you have two bank accounts, one belongs to John Smith and the other belongs to Broke Individual. You would like to transfer $20 from John Smith to Broke Individual. Assuming all first name and last name pairs are unique, this might look like:

var Fawn = require("fawn");
var task = Fawn.Task()

//assuming "Accounts" is the Accounts collection 
task.update("Accounts", {firstName: "John", lastName: "Smith"}, {$inc: {balance: -20}})
  .update("Accounts", {firstName: "Broke", lastName: "Individual"}, {$inc: {balance: 20}})
  .run()
  .then(function(){
    //update is complete 
  })
  .catch(function(err){
    // Everything has been rolled back. 

    //log the error which caused the failure 
    console.log(err);
  });

Caveat: tasks are currently not isolated(working on that) so, technically, it's possible for two tasks to retrieve and edit the same document just because that's how MongoDB works.

It's really just a generic implementation of the two phase commit example on the tutorial site: https://docs.mongodb.com/manual/tutorial/perform-two-phase-commits/

like image 34
e-oj Avatar answered Sep 20 '22 12:09

e-oj