Wednesday, June 11, 2014

Concurrency

I've reached the point where I am ready to do some coding for Project Venice.  I'm starting with the basics: client to server communications.   Node.js is a bit different the way it approaches things.  Unlike other programming languages, it is not very sequential.  Instead,  it's all about doing work when a call has completed, rather than just being next in line.  

My client (the browser) will communicate with the server (Node.js) using AJAX.  So what happens if a user double clicks a button and one request arrives before the previous one is finished?  That is the problem I am most worried about.  In fact, this issue may also affect my Google App Engine projects, but to a lesser extent.  In any case, it is time I solve this problem for good.

Here is a scenario example from the same user who is attempting to purchase some in-game good twice:

Request A: Check Funds
Request B: Check Funds
Request A: Subtract Funds
Request B: Subtract Funds

When we get to the last step, there may not be enough funds left since it doesn't know a previous request subtracted funds.  That is a big problem.  So how do we go about ensuring requests don't interfere with each other?  MongoDB, which is what I am using for this project, doesn't have a concept of transactions. Nor does it really let you lock anything specific down.  But is does provide a method to avoid the above issue.

MongoDB provides a findAndModify() function which lets you query and update a document atomically.  That means it will happens at the same time and other db operations won't happen in between.  Mongoose, the Node.JS ORM, provides wrappers for these functions: findOneandUpdate, findOneandRemove, findByIdAndRemove, findByIdAndUpdate.   An example from https://groups.google.com/forum/#!topic/mongoose-orm/5b94sq-pjsg:

var query = model.findOneAndUpdate({
      _id: 123,
      balance: { $gt: 10 }
    },
    {
      $inc: { balance: -10 }
    });

Using these functions,  I can avoid some concurrency issues.  However, these are only good for a single MongoDB document.  What happens if I am accessing multiple documents?  That is a post for another time.