Parse.com Cloud Code - saveAll Request timed out - javascript

I'm trying to save a collection of objects from a Cloud Code function.
After 30-40 objects I get a time out error. My code looks like this:
Parse.Cloud.define("saveInBackground", function (request, response) {
console.log("saveInBackground begin");
var objectsToSave = [];
for (var i = request.params.collectionToSave.length - 1; i >= 0; i--) {
objectsToSave.push(new LikedObject(request.params.collectionToSave[i]));
};
Parse.Object.saveAll(objectsToSave, {
success: function(list) {
// All the objects were saved.
if (response) {
response.success(list);
};
console.log("saveInBackground success");
},
error: function(model, error) {
// An error occurred while saving one of the objects.
if (response) {
response.error(error);
};
console.log("saveInBackground error: " + error.message);
}
});
console.log("saveInBackground end");
});
Can I do something else in order to save a bunch of objects?

Depending on the size of your objects you can try to save them in batches of 20-30. This is required since saveAll() is trying to upload all the objects you give it to the server. Here's sample code of how you might do it:
var result = true;
for (var i = request.params.collectionToSave.length - 1; i >= 0; i--) {
objectsToSave.push(new LikedObject(request.params.collectionToSave[i]));
if (i % 10 == 0) {
result = saveObjects(objectsToSave);
objectsToSave.length = 0;
}
};
if (result == true) {
console.log("saveInBackground success");
}
function saveObjects(objects) {
Parse.Object.saveAll(...);
...
}

Related

Another Parse 'success/error not called' error

I recently posted an issue I had with another Parse CloudCode method, were the error was thrown that Error: success/error was not called. I am having that issue again but with a different method/scenario.
Parse.Cloud.define("background", function(request, response) {
var moments = require("cloud/moments.js");
var now = moments.moment();
var query = new Parse.Query("Group");
query.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var object = results[i];
var events = object.get("Events");
var getUsers = false;
for (var q = 0; q < events.length; q++) {
var e = events[q];
if (e.get("date") == now) {
getUsers = true;
break;
}
}
if (getUsers == true) {
for (var q = 0; q < events.length; q++) {
var e = events[q];
if (e.get("date") == now) {
var relation = object.relation("created");
var partOne = e.get("name");
var outString1 = partOne.concat(" is now");
// generate a query based on that relation
var query = relation.query();
Parse.Push.send({
where: query, // Set our Installation query
data: {
alert: outString1
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
var relation2 = object.relation("joined");
var partOnee = e.get("name");
var outString = partOnee.concat(" is now");
// generate a query based on that relation
var query2 = relation.query();
Parse.Push.send({
where: query2, // Set our Installation query
data: {
alert: outString
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
e.destroy();
}
}
}
}
}
});
response.success();
});
Since this method involves more than just a simple query and return (as it has the for loop among other things) I am a bit confused on how to implement the Parse Promise stuff. If anyone could assist me in how I should go about implementing the promise stuff it would be much appreciated.
Parse documentation is very clear on how to use Promises and how to rewrite your pyramid code with .then() blocks instead.

Change URLs into files (Parse.com using Javascript CloudCode)

I need to batch change a number of image links (URL's links that exist within a class in) to image files (that Parse.com hosts).
Cloud code is (apparently) how to do it.
I've followed the documentation here but haven't had any success.
What I wanted to do is:
Take URL link from "COLUMN_1"
Make it a file
Upload file to "COLUMN_1" (overwrite existing URL). If this is dangerous- can upload it to a new column ("COLUMN_2").
Repeat for next row
This code did not work (this is my first time with JS):
imgFile.save().then(function () {
object.set("COLUMN_1", imgFile);
return object.save();
}).then(function (CLASSNAME) {
response.success("saved object");
}, function (error) {
response.error("failed to save object");
});
Can anyone recommend how to do this?
OK- this successfully works for anyone else trying.
Parse.Cloud.job("convertFiles", function(request, status) { //Cuts the rundata out of poor runs
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
// Tell the JS cloud code to keep a log of where it's upto. Manually create one row (in class "debugclass") to get an object Id
Parse.Cloud.useMasterKey();
var Debug = Parse.Object.extend("debugclass");
var queryForDebugObj = new Parse.Query(Debug);
queryForDebugObj.equalTo("objectId", "KbwwDV2S57");
// Query for all users
// var queryForSublist = new Parse.Query(Parse.Object.extend("gentest"));
queryForDebugObj.find({
success: function(results) {
var debugObj = results[0];
var processCallback = function(res) {
var entry = res[0];
var debugObj = results[0];
debugObj.set("LastObject", entry.id);
debugObj.save();
Parse.Cloud.httpRequest({
url: entry.get("smallImage2"),
method: "GET",
success: function(httpImgFile)
{
console.log("httpImgFile: " + String(httpImgFile.buffer));
var imgFile = new Parse.File("picture.jpg", {base64: httpImgFile.buffer.toString('base64')});
imgFile.save().then(function () {
console.log("2");
entry.set("smallImage1", imgFile);
entry.save(null, {
success: function(unused) {
debugObj.increment("itemDone");
sleep(20);
res.shift();
if (res.length === 0) {
process(entry.id);
return;
}
else {
processCallback(res);
}
},
error: function(unused, error) {
response.error("failed to save entry");
}
});
});
},
error: function(httpResponse)
{
console.log("unsuccessful http request");
response.error(responseString);
}
});
};
var process = function(skip) {{
var queryForSublist = new Parse.Query("genpants");
if (skip) {
queryForSublist.greaterThan("objectId", skip);
console.error("last object retrieved:" + skip);
}
queryForSublist.ascending("objectId");
queryForSublist.find().then(function querySuccess(res) {
processCallback(res);
}, function queryFailed(reason) {
status.error("query unsuccessful, length of result " + result.length + ", error:" + error.code + " " + error.message);
});
}};
process(debugObj.get("LastObject"));
},
error: function(error) {
status.error("xxx Uh oh, something went wrong 2:" + error + " " + error.message);
}
});
});

Getting JSON from a Function in javascript

So this will be a lot of code, but what matter is on line 22-25, and line 87-91. The rest of the code works. I have a nested function and want to return the JSON string. Using console.log I can tell it is running properly but will not return the JSON string. Look for the part that says //---------this part-------. There are two parts that I am asking about.
exports.post = function(request, response) {
var mssql = request.service.mssql;
//var data = '{"userID":"ryan3030#vt.edu1"}';
var inputJSON = request.body.JSONtext;
var json = JSON.parse(inputJSON);
var userID = json.userID;
mssql.query("EXEC getMeetingInfo ?", [userID],
{
success: function(results3) {
var meetingsToday = results3.length;
var meetingID = results3[0].meetingID;
var meetingName = results3[0].meetingName;
var meetingDescription = results3[0].meetingDescription;
var meetingLength = results3[0].meetingLength;
var meetingNotes = results3[0].meetingNotes;
var hostUserID = results3[0].hostUserID;
//--------------------------------------THIS PART------------------------------
var JSONfinal = allInfoFunction(mssql, meetingID, userID, meetingName, meetingDescription, meetingLength, meetingNotes, hostUserID, meetingsToday);
console.log(JSONfinal);//DOES NOT WORk
response.send(statusCodes.OK, JSONfinal);//DOES NOT WORK
//---------------------------------BETWEEN THESE----------------------------------
},
error: function(err) {
console.log("error is: " + err);
response.send(statusCodes.OK, { message : err });
}
});
};
function allInfoFunction(mssql, meetingID, userID, meetingName, meetingDescription, meetingLength, meetingNotes, hostUserID, meetingsToday){
mssql.query("EXEC getLocation ?", [meetingID],
{ success: function(results2) {
var meetingLocation = results2[0].meetingLocation;
var JSONlocation = {"meetingLocation": meetingLocation};
mssql.query("EXEC getDateTime ?", [meetingID],
{ success: function(results1) {
var length = results1.length;
var dateTime = [];
dateTime[0] = results1[0].meetingDateTime;
for (var x= 1; x < length; x++) {
dateTime[x] = results1[x].meetingDateTime;
}
//console.log(dateTime);
mssql.query("EXEC getDateTimeVote",
{ success: function(results) {
//console.log(results);
var JSONoutput2 = {};
var JSONtemp = [];
var length2 = results.length;
for(var j = 0; j < length; j++){
var vote = false;
var counter = 0;
for(var z = 0; z < length2; z++){
var a = new Date(results[z].meetingDateTime);
var b = new Date(results1[j].meetingDateTime);
if(a.getTime() === b.getTime()){
counter = counter + 1;
}
if((a.getTime() === b.getTime()) && (results[z].userID == userID)){
vote = true;
}
}
var meetingTimeInput = {"time": b, "numVotes": counter, "vote": vote}
JSONtemp.push(meetingTimeInput);
JSONoutput2.meetingTime = JSONtemp;
}
var JSONfinal = {};
var mainInfoArray = [];
var JSONmainInfo = {meetingID: meetingID, meetingName: meetingName, meetingDescription: meetingDescription, meetingLength: meetingLength, meetingNotes: meetingNotes, hostUserID: hostUserID, meetingLocation: meetingLocation };
JSONmainInfo.meetingTime = JSONtemp;
JSONfinal.numMeetingsToday = meetingsToday;
mainInfoArray.push(JSONmainInfo);
JSONfinal.meetingList = mainInfoArray;
//response.send(statusCodes.OK, JSONfinal);
//---------------------------------------AND THIS PART-------------------------------
console.log(JSON.stringify(JSONfinal));//This outputs the correct thing
var lastOne = JSON.stringify(JSONfinal);
return lastOne; //ths dosent work
//-------------------------------------BETWEEN THESE-----------------------------------
},
error: function(err) {
console.log("error is: " + err);
//response.send(statusCodes.OK, { message : err });
}
});
},
error: function(err) {
console.log("error is: " + err);
//response.send(statusCodes.OK, { message : err });
}
});
},
error: function(err) {
console.log("error is: " + err);
//response.send(statusCodes.OK, { message : err });
}
});
}
I've done a little refactoring to your code to take a more modular approach. It becomes quite tricky to debug something like what you have above. Here it is:
exports.post = function(request, response) {
var mssql = request.service.mssql;
var inputJSON = request.body.JSONtext;
var json = JSON.parse(inputJSON);
var userID = json.userID;
var JSONFinal = {};
getMeetingInfo(userID);
function getMeetingInfo(userID){
mssql.query("EXEC getMeetingInfo ?", [userID], {
success: function(results){
JSONFinal.meetingsToday = results.length;
JSONFinal.meetingID = results[0].meetingID;
JSONFinal.meetingName = results[0].meetingName;
JSONFinal.meetingDescription = results[0].meetingDescription;
JSONFinal.meetingLength = results[0].meetingLength;
JSONFinal.meetingNotes = results[0].meetingNotes;
JSONFinal.hostUserID = results[0].hostUserID;
// Call next function
getLocation(JSONFinal);
},
error: function(err) {
console.log("error is: " + err);
response.send(statusCodes.OK, { message : err });
}
});
}
function getLocation(){
mssql.query("EXEC getLocation ?", [JSONFinal.meetingID], {
success: function(results) {
JSONFinal.meetingLocation = results[0].meetingLocation;
// Call next function
getDateTime(JSONFinal);
},
error: function(err) {
console.log("error is: " + err);
}
});
}
function getDateTime(){
mssql.query("EXEC getDateTime ?", [JSONFinal.meetingID], {
success: function(results) {
var length = results.length;
var dateTime = [];
for (var x= 0; x < length; x++) {
dateTime[x] = results[x].meetingDateTime;
}
// Call next function
getDateTimeVote(dateTime);
},
error: function(err){
console.log("error is: " + err);
}
});
}
function getDateTimeVote(dateTime){
mssql.query("EXEC getDateTimeVote", {
success: function(results) {
var JSONtemp = [];
var length2 = results.length;
for(var j = 0; j < dateTime.length; j++){
var vote = false;
var counter = 0;
for(var z = 0; z < length2; z++){
var a = new Date(results[z].meetingDateTime);
var b = new Date(results1[j].meetingDateTime);
if(a.getTime() === b.getTime()){
counter = counter + 1;
}
if((a.getTime() === b.getTime()) && (results[z].userID == userID)){
vote = true;
}
}
var meetingTimeInput = {"time": b, "numVotes": counter, "vote": vote}
JSONtemp.push(meetingTimeInput);
}
var JSONmainInfo = {
meetingID: JSONFinal.meetingID,
meetingName: JSONFinal.meetingName,
meetingDescription: JSONFinal.meetingDescription,
meetingLength: JSONFinal.meetingLength,
meetingNotes: JSONFinal.meetingNotes,
hostUserID: JSONFinal.hostUserID,
meetingLocation: JSONFinal.meetingLocation
meetingTime: JSONtemp
};
var JSONfinal = {
numMeetingsToday: JSONFinal.meetingsToday,
meetingsList: [JSONmainInfo]
};
// Call next function
sendResponse(JSON.stringify(JSONfinal));
},
error: function(err) {
console.log("error is: " + err);
}
});
}
function sendResponse(data){
response.send(statusCodes.OK, data);
}
};
It looks a lot different but the functionality is pretty much the same. The key point if you look at the code is that each subsequent function is executed only after the success of the previous function. This effectively chains the methods together so that they are always executed in order and is the key point of difference.
One thing to note here is the similarity between your
allInfoFunction(...
and my
getMeetingInfo(userID)
Both of these will return undefined more or less immediately, after the MSSQL query is sent to the server. This is because the request is fired asynchronously, and the javascript continues to run through the code. After it's fired of the asynchronous request, it has nothing left to do in that function, so it returns. There is no return value specified so it returns nothing (or undefined if you will).
So all in all, the code in the question code will return undefined and then attempt to send a response before anything has actually happened. This code fixes that problem by firing the sendResponse after all the queries have been executed.
I refactored this code in a text editor and haven't run it, or checked it for errors, so follow the basic outline all you like but it may not be a good idea to copy and paste it without checking it's not broken

How to correct structure an asynchronous program to ensure correct results?

I have a nodejs program that requests a series of XML files, parses them and then puts the output in an array which is written to disk as a CSV file.
The program mostly works, however occasionally the files end up in the wrong order in the array.
I want the order of the results to be in the same as the order as the URLs. The URLs are stored in an array, so when I get the XML file I check what the index of the URL was in the source array and insert the results at the same index in the destination URL.
can anyone see the flaw that is allowing the results to end up in the wrong order?
addResult = function (url, value, timestamp) {
data[config.sources.indexOf(url)] = {
value : value,
timestamp : timestamp,
url : url
};
numResults++;
if (numResults === config.sources.length) { //once all results are in build the output file
createOutputData();
}
}
fs.readFile("config.json", function (fileError, data) {
var eachSource, processResponse = function (responseError, response, body) {
if (responseError) {
console.log(responseError);
} else {
parseXML(body, {
explicitArray : false
}, function (xmlError, result) {
if (xmlError) {
console.log(xmlError);
}
addResult(response.request.uri.href, result.Hilltop.Measurement.Data.E.I1, moment(result.Hilltop.Measurement.Data.E.T));
});
}
};
if (fileError) {
console.log(fileError);
} else {
config = JSON.parse(data); //read in config file
for (eachSource = 0; eachSource < config.sources.length; eachSource++) {
config.sources[eachSource] = config.sources[eachSource].replace(/ /g, "%20"); //replace all %20 with " "
request(config.sources[eachSource], processResponse); //request each source
}
}
});
var writeOutputData, createOutputData, numResults = 0, data = [], eachDataPoint, multipliedFlow = 0;
writeOutputData = function (output, attempts) {
csv.writeToPath(config.outputFile, [ output ], {
headers : false
}).on("finish", function () {
console.log("successfully wrote data to: ", config.outputFile);
}).on("error", function (err) { //on write error
console.log(err);
if (attempts < 2) { //if there has been less than 3 attempts try writing again after 500ms
setTimeout(function () {
writeOutputData(output, attempts + 1);
}, 500);
}
});
};
createOutputData = function () {
var csvTimestamp, output = [];
if (config.hasOwnProperty("timestampFromSource")) {
csvTimestamp = data.filter(function (a) {
return a.url === config.sources[config.timestampFromSource];
})[0].timestamp.format("HHmm");
console.log("timestamp from source [" + config.timestampFromSource + "]:", csvTimestamp);
} else {
csvTimestamp = data.sort(function (a, b) { //sort results from oldest to newest
return a.timestamp.unix() - b.timestamp.unix();
});
csvTimestamp = csvTimestamp[0].timestamp.format("HHmm");//use the oldest date for the timestamp
console.log("timestamp from oldest source:", csvTimestamp);
}
//build array to represent data to be written
output.push(config.plDestVar); //pl var head address first
output.push(config.sources.length + 1); //number if vars to import
output.push(csvTimestamp); //the date of the data
for (eachDataPoint = 0; eachDataPoint < data.length; eachDataPoint++) { //add each data point
if (config.flowMultiplier) {
multipliedFlow = Math.round(data[eachDataPoint].value * config.flowMultiplier); //round to 1dp and remove decimal by *10
} else {
multipliedFlow = Math.round(data[eachDataPoint].value * 10); //round to 1dp and remove decimal by *10
}
if (multipliedFlow > 32766) {
multipliedFlow = 32766;
} else if (multipliedFlow < 0) {
multipliedFlow = 0;
}
output.push(multipliedFlow);
}
console.log(output);
writeOutputData(output, 0); //write the results, 0 is signalling first attempt
};
I think that the url to index code needs debugging.
Here is an example that uses an object that is pre-populated with keys in the for loop.
`
var http = require('http');
var fs = require("fs");
var allRequestsComplete = function(results){
console.log("All Requests Complete");
console.log(results);
};
fs.readFile("urls.json", function (fileError, data) {
var responseCount = 0;
if (fileError) {
console.log(fileError);
} else {
var allResponses = {};
config = JSON.parse(data); //read in config file
var requestComplete = function(url, fileData){
responseCount++;
allResponses[url] = fileData;
if(responseCount===config.sources.length){
allRequestsComplete(allResponses);
}
};
for (var eachSource = 0; eachSource < config.sources.length; eachSource++) {
(function(url){
allResponses[url] = "Waiting";
http.get({host: url,path: "/"}, function(response) {
response.on('error', function (chunk) {
requestComplete(url, "ERROR");
});
var str = ''
response.on('data', function (chunk) {
str += chunk;
});
response.on('end', function () {
requestComplete(url, str);
});
});
}(config.sources[eachSource].replace(/ /g, "%20").replace("http://", "")));
}
}
});
`
I agree with #Kevin B, you cannot assume that async callbacks will return in the same order of which you send them. However, you could ensure the order, by adding an index function on processResponse.
say you add the following to addResult
addResult = function (index, url, value, timestamp) {
data[index] = {
value : value,
timestamp : timestamp,
url : url
};
numResults++;
if (numResults === config.sources.length) { //once all results are in build the output file
createOutputData();
}
}
and use an extra function to call your request
function doRequest(index, url) {
request(url, function(responseError, response, body) {
if (responseError) {
console.log(responseError);
} else {
parseXML(body, {
explicitArray : false
}, function (xmlError, result) {
if (xmlError) {
console.log(xmlError);
}
addResult(index, response.request.uri.href, result.Hilltop.Measurement.Data.E.I1, moment(result.Hilltop.Measurement.Data.E.T));
});
}
});
}
then you can also change your loop to:
for (eachSource = 0; eachSource < config.sources.length; eachSource++) {
config.sources[eachSource] = config.sources[eachSource].replace(/ /g, "%20"); //replace all %20 with " "
doRequest(eachSource, config.sources[eachSource]); //request each source
}

Cloud code skipping section of code

I never learnt javascript so please bear with me.
I have a cloud function that does all but the indicated section (>>):
Parse.Cloud.define("acceptRequest", function(request, response) {
var user = request.user;
var requestUser;
var requestObject;
var requestId = request.params.objectId;
var query = new Parse.Query(GameRequests);
query.get(requestId,{
//Get GameRequest Object - requestObject
success: function (object) {
var requestObject = object;
var sender = requestObject.get("from");
var senderId = sender[0];
var query = new Parse.Query(Parse.User);
query.get(senderId, {
//From GameRequest Object data, find sender of request "requestUser"
success: function (userObject) {
var requestUser = userObject;
var requestUserList = requestUser.get("SENTrequests")
var requestIndex = requestUserList.indexOf(requestId);
if (requestIndex > -1) {
requestUserList.splice(requestIndex, 1);
requestUser.set("SENTrequests",requestUserList);
if (requestUserList.length = 1) {
user.unset("SENTrequests");
}
}
var userList = user.get("RCDrequests");
var userIndex = userList.indexOf(requestId);
if (userIndex > -1) {
userList.splice(userIndex, 1);
user.set("RCDrequests",userList);
if (userList.length = 1) {
user.unset("RCDrequests");
}
}
requestObject.destroy({
success: function(requestObject) {
requestUser.add("partners",user.id);
user.add("partners",requestUser.id);
var firstmsg = new GameMessage();
var secondmsg = new GameMessage();
firstmsg.set("sender", user.id);
firstmsg.set("receiver", requestUser.id);
firstmsg.set("sent", 0);
firstmsg.set("received", 0);
firstmsg.set("receiverName", requestUser.getUsername());
secondmsg.set("sender", requestUser.id);
secondmsg.set("receiver", user.id);
secondmsg.set("sent", 0);
secondmsg.set("received", 0);
secondmsg.set("receiverName", user.getUsername());
Parse.Object.saveAll([requestUser, user, firstmsg, secondmsg], { useMasterKey: true },{
>> success: function() {
>> console.log("Saving messages again");
>> firstmsg.set("otherside", secondmsg.id);
>> secondmsg.set("otherside", firstmsg.id);
>>
>> Parse.Object.saveAll([firstmsg, secondmsg]);
},
error: function(error) {
}
});
response.success("Successful");
},
error: function(requestObject, error){
}
});
},
error: function (userObject,error) {
}
});
},
error: function (object,error) {
response.error("Error");
}
});
});
It is supposed to save the two messages' objectIds so each has a reference to the other.
What is causing this problem and how can I fix it?
Thank you
I think your sending your response.success("Successful") before the save has been completed. Move your response to the success handler of the save.
You should take a look at promises section. You will not need the deep nested functions you have currently.

Categories

Resources