问题
In my parse server I have a class called Stats
which contains the columns secondsPlayed
(number) and timeScore
(number)
I am using cloud code to update all the rows in the column timeScore
The code below works only when updating and saving 1 or 2 objects results.length
. If Parse.Query returns more than 2 results the code crashes and I get the following error.
error: Failed running cloud function timeScore for user undefined with:
Input: {}
Error: {"code":101,"message":"Object not found."} functionName=timeScore, code=101, message=Object not found., , user=undefined
error: Error generating response. ParseError { code: 101, message: 'Object not found.' } code=101, message=Object not found.
error: Object not found. code=101, message=Object not found.
This is a problem as I need to update and save thousands of objects. What is the best and fastest way to do this?
Why does my code work for 2 objects but not for more than 2? How can I fix this?
Here is my code
var _ = require("underscore");
Parse.Cloud.define("timeScore", function(request, response) {
var query = new Parse.Query("Stats");
query.greaterThan("secondsPlayed", 1000);
query.find().then(function(results) {
_.each(results, function(result) {
var secondsPlayed = result.get("secondsPlayed") || 0;
result.set("timeScore", secondsPlayed*2);
});
return Parse.Object.saveAll(results);
}).then(function(results) {
response.success(results);
}, function(error) {
response.error(error);
}); });
Here is how I call it
#!/usr/bin/env node
var Parse = require("parse/node");
Parse.initialize("xx", "xx");
Parse.serverURL = "http://randomapp.herokuapp.com/parse";
Parse.Cloud.run('timeScore');
UPDATE:
Below is my latest code. Everything works well except for the fact that I get the following error for no apparent reason.
heroku[router]: at=error code=H12 desc="Request timeout" method=POST path="/parse/functions/timeScore
I get the timeout error regardless of what batchSize I choose and I get it every 30 seconds. I get it a total of 5 times. After the 5th time I dont get it anymore. I get the 5th and last error at around 2.5 minutes into the process (30seconds*5). This error does not effect the process in any way. All 250k objects are updated and saved regardless of batchSize.
I thought maybe because I never call results.error
or results.success
the server thinks I am still doing some work and shows the error. However I updated my code as the following and I still get the timeout errors.
Also after each timeout error processBatch() is called once again from the beggining. Since I get 5 timeout errors processBatch() gets called 5 times. So after the 5th timeout error there is 5 processBatch() functions running simultaneously (I confirmed this with logs).
What is causing the heroku timeout errors I am getting? How do I fix it?
var _ = require("underscore");
Parse.Cloud.define("timeScore", function(request, response) {
var counter = 0;
function processBatch(query, batchSize, startingAt, process) {
query.limit(batchSize);
query.skip(startingAt);
return query.find().then(results => {
return process(results).then(() => results.length);
}).then(length => {
return (length === batchSize)? processBatch(query, batchSize, startingAt+length, process) : {};
});
}
function setTimeScores(stats) {
console.log("LENGTH " + stats.length);
_.each(stats, stat => {
counter ++;
stat.set("timeScore", counter);
});
return Parse.Object.saveAll(stats);
}
var query = new Parse.Query("Stats");
processBatch(query, 2500, 0, setTimeScores).then(results => {
response.success(results);
}).catch(error => {
response.error(error);
});
});
回答1:
To handle numbers of objects greater than the max query limit, build a more abstract function that uses query's limit()
and skip()
to cursor through the data:
function processBatch(query, batchSize, startingAt, process) {
query.limit(batchSize);
query.skip(startingAt);
return query.find().then(results => {
return process(results).then(() => results.length);
}).then(length => {
return (length === batchSize)? processBatch(query, batchSize, startingAt+length, process) : {};
});
}
This says, get one, batchSize
-long batch of objects specified by query
, then do something with the retrieved objects, then, if there might be more, do the same thing again, skipping objects we've processed already.
Your process step looks like this:
function setTimeScores(stats) {
_.each(stats, stat => {
var secondsPlayed = stat.get("secondsPlayed") || 0;
stat.set("timeScore", secondsPlayed*2);
});
return Parse.Object.saveAll(stats);
}
Call it like this:
let query = new Parse.Query("Stats");
query.greaterThan("secondsPlayed", 1000);
processBatch(query, 100, 0, setTimeScores);
EDIT in the context of a cloud function, call it like this...
Parse.Cloud.define("computeStats", function(request, response) {
let query = new Parse.Query("Stats");
query.greaterThan("secondsPlayed", 1000);
processBatch(query, 100, 0, setTimeScores).then(results => {
response.success(results);
}).catch(error => {
response.error(error);
});
});
来源:https://stackoverflow.com/questions/50991523/updating-and-saving-thousands-of-objects-at-once-on-parse-server-using-cloud-cod