javascriptparse-platformparse-cloud-codeafter-save

Parse afterSave function getting skipped over


so i have a messaging app using parse.com as my backend. When i send a message from the app it saves it on Parse.com to a class called "NewMessages". Then in my cloud code i have an afterSave function dedicated to this class so that when a new object gets saved to "NewMessages" it picks a random user attaches it to the message and saves it in a new class called "Inbox". Then it deletes the original message from "NewMessages".

So the "NewMessages" class should always be empty right? But when I send a bunch of messages very quickly some get skipped over. How do i fix this?

Is there a better way to structure this than using afterSave?

function varReset(leanBody, leanSenderName, leanSenderId, randUsers){
   leanBody = "";
   leanSenderName = "";
   leanSenderId = "";
   randUsers = [];
   console.log("The variables were set");
}


Parse.Cloud.afterSave("Lean", function(leanBody, leanSenderName, leanSenderId, randUsers, request) {
  varReset(leanBody, leanSenderName, leanSenderId, randUsers);

  var query = new Parse.Query("NewMessages");
  query.first({
    success: function(results){
      leanBody = (results.get("MessageBody"));
      leanSenderName = (results.get("senderName"));
      leanSenderId = (results.get("senderId"));
      getUsers(leanBody, leanSenderName, leanSenderId);
      results.destroy({
        success: function(results){
          console.log("deleted");
        }, error: function(results, error){
        }
      });
    }, error: function(error){
    }

  });
});

  function getUsers(leanBody, leanSenderName, leanSenderId, response){

    var query = new Parse.Query(Parse.User);
    query.find({
        success: function(results){
            var users = [];
            console.log(leanBody);
            console.log(leanSenderName);

            //extract out user names from results
            for(var i = 0; i < results.length; ++i){
                users.push(results[i].id);
            }
            for(var i = 0; i < 3; ++i){
                var rand = users[Math.floor(Math.random() * users.length)];
                var index = users.indexOf(rand);
                users.splice(index, 1);
                randUsers.push(rand);
                }
            console.log("The random users are " + randUsers);
            sendMessage(leanBody, leanSenderName, leanSenderId, randUsers);
        }, error: function(error){
            response.error("Error");
        }
    });
  }

  function sendMessage(leanBody, leanSenderName, leanSenderId, randUsers){
    var Inbox = Parse.Object.extend("Inbox");

    for(var i = 0; i < 3; ++i){

      var inbox = new Inbox();
      inbox.set("messageBody", leanBody);
      inbox.set("senderName",  leanSenderName);
      inbox.set("senderId", leanSenderId);
      inbox.set("recipientId", randUsers[i]);
      console.log("leanBody = " + leanBody);
      console.log("leanSenderName = " + leanSenderName);
      console.log("leanSenderId = " + leanSenderId);
      console.log("recipient = " + randUsers[i]);

      inbox.save(null, {
        success: function(inbox) {
          // Execute any logic that should take place after the object is saved.
          alert('New object created with objectId: ' + inbox.id);
        },
        error: function(inbox, error) {
          // Execute any logic that should take place if the save fails.
          // error is a Parse.Error with an error code and message.
          alert('Failed to create new object, with error code: ' + error.message);
        }
      });
    }
  }

Solution

    1. Have you checked your logs? You may be falling afoul of resource limits (https://parse.com/docs/cloud_code_guide#functions-resource). If immediacy is not important, it may be worth looking into set up a background job that runs every few minutes and tackles undelivered messages. It may also be possible to combine the two approaches: having the afterSave function attempt to do an immediate delivery to Inboxes, while the background job picks up any NewMessages left over on a regular basis. Not the prettiest solution but at least you have a bit more reliability. (You'll have to think about race conditions though where the two may attempt deliveries on the same NewMessage.)

    2. Regarding your question about a better structure, if the two classes are identical (or close enough), is it possible to just have a Messages class? Initially the "to" field will be null but is assigned a random recipient on a beforeSave function. This may be faster and neater.

    EDIT: Adding a 3rd observation which was originally a comment:

    I saw that you are using a Query.first() in afterSave in order to find the NewMessage to take care of. Potentially, a new NewMessage could have snuck in between the time afterSave was called, and the Query was run. Why not get the ID of the saved NewMessage and use that in the Query, instead of first()?

    query.get(request.object.id,...);

    This ensures that the code in afterSave handles the NewMessage that it was invoked for, not the one that was most recently saved.