micnil
micnil

Reputation: 4805

Update two documents in different models in one request

Say that I am developing the back-end for a Polling app. The user selects an option in a poll and a request to update both the poll document and the users document is made.

// Express endpoint for /vote/:pollId/:optionIndex
// (middleware puts users document on req.user)
function(req, res) {

    //Check if the user has already voted on this poll
    ...

    Polley.findByIdAndUpdate(req.params.pollId, {
        //increment the number of votes on the selected options by one
        $inc: ...
    }, {
        new: true
    }, function (err, data) {
        //If err send error message
        ...

        // Now update the users document, so that the user 
        // can't vote on this poll again and so the user can go
        // back and see what he/she voted for.
        User.findByIdAndUpdate( req.user._id,
            {
                $push: {
                    votes: {
                        polleyId: req.params.polleyId,
                        optionIndex: req.params.optionIndex
                    }
                }
            },
            function (err, user) {
                // HERE IS THE PROBLEM. 
                // what if error happens here? Then the user will be able
                // to vote on this poll one more time since the vote was not
                // successfully saved in the users document.
            }
        );

        res.status(200).json({ poll_votes: data.votes, message: "Vote Successful." });
    });
}

I removed parts with ... because the implementation is not relevant to the question.

Both of these operations need to be made in one call so that the user does not have the power to only update the poll document but not the user document.

The problem, as i stated as a comment in the code, is that if i first update the poll document and afterwards update the user document, the first operation could be successful while the second is not.

How do I ensure that both operations either fails or succeeds?

Upvotes: 0

Views: 101

Answers (1)

Artem Baranovskii
Artem Baranovskii

Reputation: 1003

MongoDB doesn't support atomic update of different collections and transactions. To answer your question I see two approaches:

  1. Simulate transaction against different collections by using Two Phase Commits
  2. Migrate your data structure to patching way that allows you make atomic operations (like Google Docs does).
  3. I could advise to use TokuMX (it supports transactions) instead MongoDB but in question's tags you pointed nodejs in which using Tokumx for this purpose not possible due transactions are associated with a thread.

If you need to support transactions and need to use MongoDB I would advice to try Two Phase Commits because it's simplest way to resolve your task.

Upvotes: 1

Related Questions