Collection.findOne() not really sync?


#1

Hi,
I am looping through json and insert posts into a collection.
I create a struct like myPost = {_id: json._id, bla: json.bla}

Problem is that the data in my jsons might be duplicated, so I check:

if(typeof myColl.findOne({_id: myPost._id}) === "undefined"){
        try{
            myColl.insert(myPost);
            } catch(e){console.log(e)}

}

Still I get “duplicate key” in my catch. What do I do wrong?


#2

There is a typo in your code. You say your myPost has an id field, but you are referencing it as myPost._id in your test (should be myPost.id). It’s probably that, but …

Obviously, you should really be doing this on the server, not the client (you don’t say where this code lives). You also do not seem to be addressing potential race conditions (your test/insert is not atomic).


#3

Oh, the underscore was invisible when I did not wrap it in PRE, they are the same in the real code.

Race condition you say, yes, if findOne is async, but from what I understand it should be sync and then there’s no race.


#4

No, it is synchronous. I mean if this code is being run by multiple instances, then in the time between the test being passed and the insert being run in one instance, it’s possible for another instance to also pass the test and try the insert.

Where is this code being run?


#5

It is being run within a Meteor.call()
One single call on my development machine, so unless it threads & races itself there should be no collisions and if it does, the odds are pretty small, IMO. But maybe not. Let me think twice about that :slight_smile:


#6

Can you share your complete method code?


#7

I think you’re right. I think a need to use Future and wait for the insert call to finish. Otherwise it will race, just as you say.


#8

Well, I’ve just tested a naive example and it works correctly (no futures).

Meteor.methods({
  load: function() {
    const data = [
      {_id: 'abc', value: 123},
      {_id: 'abc', value: 123},
      {_id: 'abc', value: 123},
      {_id: 'def', value: 321},
      {_id: 'def', value: 321},
      {_id: 'abc', value: 123},
      {_id: 'abc', value: 123},
      {_id: 'abc', value: 123},
      {_id: 'def', value: 321},
      {_id: 'abc', value: 123},
      {_id: 'abc', value: 123},
      {_id: 'abc', value: 123},
      {_id: 'def', value: 321},
      {_id: 'def', value: 321},
      {_id: 'def', value: 321},
      {_id: 'abc', value: 123},
      {_id: 'abc', value: 123},
      {_id: 'def', value: 321},
      {_id: 'def', value: 321},
    ];

    _.each(data, function(doc) {
      if (typeof MyCollection.findOne({_id: doc._id}) === 'undefined') {
        try {
          MyCollection.insert(doc);
        } catch(err) {
          console.log(err);
        }
      }
    });
  }
});

I don’t get any console logs and just two documents inserted.


#9

Yes, that is what I expected to happen for me too. Maybe, since I run many thousand of items, the inserts slows down to the point where it might not be finished until the duplicates knocks on the door.

But thanks a lot for your help, you opened my mind!