in my server/server.js
Meteor.methods({
saveOnServer: function() {
var totalCount = Collections.find({
"some": "condition"
}).count();
if (totalCount) {
var customerId = Collections.update('someId', {
"$addToSet": {
objects: object
}
}, function(err) {
if (err) {
throw err;
} else {
return true;
}
});
} else {}
}
});
I'm afraid that when saveOnServer() is called by 2 clients at the same time, it will return the same totalCount for each client and basically end up inserting same integer number into object id. The end goal is to insert row on the server side with an atomic operation that only completes when the totalCount
is successfully returned and the document is inserted ensuring that no duplicate id exists? I'm trying to not use the mongodb _id but have my own integer incrementing id column.
I'm wondering how I can ensure that a field gets auto-incremented for each insert operation? I am currently relying on getting the total count of documents. Is a race condition possible here? If so, what is the meteor way of dealing with this?
In Meteor's concurrency model, you can imagine a whole method as an uninterruptible block of stuff that happens. In order for Meteor to switch from running one method midway to say, starting another method, you need to "yield"—the method needs to signal, "I can be interrupted."
Methods yield whenever they do something asynchronous, which in practice means any time you do a database update or call a method with a callback in Meteor 0.6.5 and later. Since you give your
update
call a callback, Meteor will always try to do something in between the call toupdate
and theupdate
's callback. However, in Meteor 0.6.4.2 and earlier, database updates were uninterruptible regardless of the use of callbacks.However, multiple calls to
saveOnServer
will happen in order and do not cause a race condition. You can callthis.unblock()
to allow multiple calls tosaveOnServer
to occur "simultaneously"—i.e., not share the same queue, labeledsaveOnServer queue
, of uninterruptible blocks of stuff.Given the code you have, another method modifying
Collections
can change the value ofcount()
between the call and the update.You can prevent one method from making the other invalid midway by implementing the following data models:
When adding objects to
Collections
:Note, while this may seem inefficient, it reflects the exact cost of making an update and insert in different methods behave the way you intend. In
saveOnServer
you cannot insert.Conversely, if you removed the callback from
Collections.update
, it will occur synchronously and there will be no race conditioning Meteor 0.6.5 and later.Another way to do this is from a mechanism hibernate/jpa follows - and that is to set up a collision field. Most of the time, this can be an update timestamp that is set on each update. Just prior to doing any update, query the update timestamp. Then you can specify the update where the update timestamp is what you just fetched. If it has changed in the interim, the update won't happen - and you check the return code/count that the row was updated or not. JPA does this automatically for you when you add an annotation for this collision field - but this is essentially what it does in behind the scenes
You can make this collection have a unique key on an index field, and then keep it updated as follows:
1) Whenever you insert into the collection, first do a query to get the maximum index and insert the document with index + 1.
2) To find out the number of documents just do the query to get the max of the index.
Insertion is now a pair of queries, a read and a write, so it can fail. (DB ops can always fail, though.) However, it can never leave the database in an inconsistent state - the Mongo index will guarantee that.
The syntax for building an index in Meteor is this: