One solution could be to keep a hash of query objects. Every time before 
you run a query for find, identify the query with something like 
JSON.stringify() then check if the hash includes that query already. If it 
doesn't, create an array equal to that query id with the callback as the 
only element. It it does, add the callback to that array and return.

Something like

var queries = {};
var findOrCreate = exports.findOrCreate = function (data, callback) {
  if (!data.source) {
    return callback(new ParamError('Missing account.source.'));
  }
  if (!data.external_id) {
    return callback(new ParamError('Missing account.external_id'));
  }

  var query = {
    source: data.source,
    external_id: data.external_id
  };

  // check if this query was already searched for
  // and has not called its callback
  var queryID = JSON.stringify(query);
  var callbackList = queries[queryID];
  if (!callbackList) {
    callbackList = queries[queryID] = [callback];
  } else {
    callbackList.push(callback);
    return;
  }
  
  // change original callback to cleanup callback list
  callback = function (err, account) {
    callbackList.forEach(function (callback) {
      callback(err, account);
    });
    delete queries[queryID];
  };

  Account.findOne(query, function (err, account) {
    if (err) {
      return callback(err);
    }
    if (account) {
      return callback(err, account);
    }
    account = new Account(data);
    account.save(function (err) {
      return callback(err, account);
    });
    
  });

};

On Saturday, July 28, 2012 6:12:40 PM UTC-7, Jaime Morales wrote:
>
> Hi,
>
> I am running into an issue trying to run multiple findOrCreate methods in 
> parallel.
>
> Here is the code:
>
> var findOrCreate = exports.findOrCreate = function (data, callback) {
>   if (!data.source) {
>     return callback(new ParamError('Missing account.source.'));
>   }
>   if (!data.external_id) {
>     return callback(new ParamError('Missing account.external_id'));
>   }
>
>   Account.findOne({
>     source: data.source,
>     external_id: data.external_id
>   }, function (err, account) {
>     if (err) {
>       return callback(err);
>     }
>     if (account) {
>       return callback(err, account);
>     }
>     account = new Account(data);
>     account.save(function (err) {
>       return callback(err, account);
>     });
>     
>   });
>
> };
>
> The issue is that I may have multiple queries for the same 
> source/external_id combo.  I am using async.parallel to handle them.  But 
> since the callbacks in mongoose are pushed onto the next tick, I end up in 
> a situation where 
>
>    1. a find is performed (pushing the create to the next tick)
>    2. another find is performed for the same source/external_id (pushing 
>    the create to the next tick)
>    3. a create is performed (for the first find)
>    4. an attempt is made to create again, but this time a duplicate key 
>    error is thrown (since I already created that account)
>    
> I originally tried to get around this by doing an upsert instead of find 
> or create, but that didn't work since I need access the the resulting item. 
>  I am not sure what the best way to handle this scenario is.  any ideas?
>

-- 
Job Board: http://jobs.nodejs.org/
Posting guidelines: 
https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Reply via email to