node.js - Synchronizing multiple requests and multiple database calls -


i'm dealing issue duplicate key errors when i'm creating documents in mongo database (using mongoose) in node.js application.

here scenario

an http request update particular user.

post /process { "user": 123 } 

the server checks see if user exists. if doesn't exist, creates new user, otherwise updates user , saves.

user.findone({ "user": 123 }, function (err, doc) {     if (!doc) {         doc = new user({ ... });     }     doc.updated = date.now();     doc.save(...); } 

there 2 asynchronous calls findone , save. problem i'm facing happens when second http request (for same user) comes in during 1 of asynchronous calls (i.e. before doc.save has completed first request. though node.js single threaded, can still occur if there delay during asynchronous i/o.

r1: post /process r1: findone => async r2: post /process r2: findone => async r1: !doc = true r2: !doc = true 

so, both requests application thinks user doesn't exists , hence tries create document twice same key.

how solve it?

  • well, first of all, i've minimized time between findone , save. however, problem still happening on occasions (only maybe 1/1000).

  • i don't want use upsert, since when creating new user i'm setting other fields default values. think tricky upsert.

  • ideally want make sure 1 request can enter processing logic @ once (sort of mutex around function call). however, don't want block request call - maybe there nice async lock utility use?

example:

/* don't block, call function when lock can acquired */ lock(function (done) {     /* can enter here 1 @ time */     done(); /* <-- unlocks */ } 

or, approaching in wrong way. ideas?

simple. do not use .findone() , .save() .update() object in database directly.

mongodb , "locking" ( in way) does not happen , by design. not meant to:

  1. reserve book
  2. take book shelf
  3. write new paragraph book
  4. return book shelf
  5. un-reserve book others use

this "great big no in scaleable patterns.

so instead of doing just.

"write new paragraph book on shelf"

and preferably

"write new paragraph book on shelf, current paragraph expect be".

that should "clear" analogy.

so instead do:

user.update(    { "user": 123 },    { "$set": { "updated": new date() } },    function(err,numaffected) {        // in callback    } ); 

which "simply" updates current "updated" date @ time of request made there , then.

or better:

var updateddate = new date();  user.update(    { "user": 123, "updated": { "$lt": updateddate } },    { "$set": { "updated": updateddate } },    function(err,numaffected) {        // in callback    } ); 

which wil not touch object curren value of "updated" "greater than" value want set to.

pure logic.


Comments