1

Is there a design pattern in meteor application to handle multiple clients inserting the same logical record 'simultaneously'?

Specifically I have a scoring type application, and multiple clients could create the initial, basically blank, Score record for an Entrant when the entrant is ready to start. The appearance of the record is then used to make it available on the page for editing by the officials, incrementing penalty counts and such.

Stages =  new Meteor.Collection("contests");
Entrants = new Meteor.Collection("entrants");
Scores = new Meteor.Collection("scores");

// official picks the next entrant 
Scores.insert( stage_id:xxxx, entrant_id:yyyy)

I am happy with the implications of the conflict resolutions of edits to the Score record once it is in the Collection. I am not sure how to deal with multiple clients trying to insert the Score for the stage_id/entrant_id pair.

In a synchronous app I would tend to use some form of interlocking, or a relational DB key constraint.

1 Answers1

1

Well, according to this answer Meteor $upsert flag is still in enhancement list and seems to be added in stable branch after 1.0 release.

So the first way is how it was said to add an unique index:

All the implementation ways are listed here. I would recommend you to use native mongo indexes, not a code implementation.

The optimistic concurrency way is much more complicated according to no transactions in MongoDB.

Here comes my implementation of it(be careful, might be buggy))):

var result_callback = function(_id) {
  // callback to call on successfull insert made
}

var $set = {stage_id: xxxx, entrant_id: xxxx};
var created_at = Date.now().toFixed();
var $insert = _.extend({}, $set, {created_at: created_at});
Scores.insert($insert, function(error, _id) {
  if (error) {
    //handle it
    return;
  }
  var entries = Scores.find($set, {sort: {created_at: -1}}).fetch()
  if (entries.length > 1) {
    var duplicates = entries.splice(0, entries.length - 1);
    var duplicate_ids = _.map(duplicates, function(entry) {
      return entry._id;
    });
    Scores.remove({_id: {$in: duplicate_ids}})
    Scores.update(entries[0]._id, $set, function(error) {
      if (error) {
        // handle it
      } else {
        result_callback(entries[0]._id)
      }
    })
  } else {
    result_callback(_id);
  }
});

Hope this will give you some good ideas)

Sorry, previous version of my answer was completely incorrect.

Community
  • 1
  • 1
icanhazbroccoli
  • 1,035
  • 9
  • 15
  • Those linked questions are helpful. I was mainly thinking about what the clients will see. I am now leaning towards pre-creating all the Scores for a Stage, when a user flags the stage as 'open'. This solves not only the above, but also avoids reactive churn listing items not in another collection. – Mathew Boorman Jun 01 '13 at 05:29
  • Pre-creation would be the best solution here I guess. Much simpler. Much more transparent. – icanhazbroccoli Jun 03 '13 at 08:39