Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Could this code cause race condition in Node.js?

I'm working in Sails.js (v0.12.13), and one of my actions in the controller looks like:

create: function(req, res){

    var comment = req.body;

    var image_id = req.params.id;

    Image.findOne(image_id).populate('comments').exec(function(err, image){


        image.messages.add(comment);

        image.save(function(err){

            return res.created(comment);

        });
    });
}

(Error handling was ommited)

Basically this adds a comment to an image. First, I need to get the image, with it's comments, add the new comment to the array, and save it again.

However, my intuition is that there's a case when two different people try to add a comment, and the order of events is:

  1. Request #1 does findOne() and becomes blocked
  2. Request #2 does findOne() and becomes blocked
  3. Request #1 adds a comment, executes save() and becomes blocked
  4. Request #2 adds a comment, executes save() and becomes blocked
  5. Both requests had the same original findOne() result, and they added their own comment, so when they save it, only the last one remains.

Can this happen in Node.js?, or is there something that prevents this from happening?

I've seen some examples in the website, like http://sailsjs.com/documentation/reference/waterline-orm/populated-values/add where they do something similar. If race condition can happen, then Sail.js becomes unuseable for me, because I find these things very important.

Thanks in advance.


1 Answers

Short answer: Don't worry, you'll not lose / overwrite data this way.


Can this happen in Node.js?

Yes

is there something that prevents this from happening?

Yes


This condition is not specific to Node.js or asynchronous, event loop based execution.
Similar thing can happen with 2 threads handling 2 requests in other languages (Java, Ruby etc.) due to thread preemption.

Problematic Implementation

  1. Req#1 fetches Image and gets { id: 1, comments: [1, 2] }
  2. Req#2 fetches Image and gets { id: 1, comments: [1, 2] }
  3. Req#1 adds a comment so object becomes { id: 1, comments: [1, 2, 3] }. On save, it ensures that only 1, 2, 3 comments are associated with Image 1.
  4. Req#2 adds a comment so object becomes { id: 1, comments: [1, 2, 4] }. On save, it ensures that only 1, 2, 4 comments are associated with Image 1, thereby removing comment 3

Actual Implementation

  1. Same as 1 above
  2. Same as 2 above
  3. Req#1 adds a comment so query object records Comment#3 to be added. Something like { id: 1, comments: { value: [1, 2], addModels: [3] }. On save, an association is created between Comment#3 and Image#1 in database.
  4. Req#2 adds a comment so query object records Comment#4 to be added. Something like { id: 1, comments: { value: [1, 2], addModels: [4] }. On save, an association is created between Comment#4 and Image#1 in database. Earlier created association is Comment#3 is not touched.

Relevant code:

  1. Waterline association.js
  2. Waterline save.js
like image 76
Sangharsh Avatar answered Sep 10 '26 18:09

Sangharsh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!