updated by GasGit automation
This commit is contained in:
parent
e9949aec45
commit
c88e78cba9
1 changed files with 138 additions and 0 deletions
138
scripts/Utils.js
138
scripts/Utils.js
|
@ -115,7 +115,145 @@ var Utils = (function (ns) {
|
|||
*/
|
||||
ns.isVanObject = function (value) {
|
||||
return typeof value === "object" && !Array.isArray(value);
|
||||
};
|
||||
/**
|
||||
* recursive rateLimitExpBackoff()
|
||||
* @param {function} callBack some function to call that might return rate limit exception
|
||||
* @param {object} options properties as below
|
||||
* @param {number} [attempts=1] optional the attempt number of this instance - usually only used recursively and not user supplied
|
||||
* @param {number} [options.sleepFor=750] optional amount of time to sleep for on the first failure in missliseconds
|
||||
* @param {number} [options.maxAttempts=5] optional maximum number of amounts to try
|
||||
* @param {boolean} [options.logAttempts=true] log re-attempts to Logger
|
||||
* @param {function} [options.checker] function to check whether error is retryable
|
||||
* @param {function} [options.lookahead] function to check response and force retry (passes response,attemprs)
|
||||
* @return {*} results of the callback
|
||||
*/
|
||||
|
||||
ns.expBackoff = function ( callBack,options,attempts) {
|
||||
|
||||
//sleepFor = Math.abs(options.sleepFor ||
|
||||
|
||||
options = options || {};
|
||||
optionsDefault = {
|
||||
sleepFor: 750,
|
||||
maxAttempts:5,
|
||||
checker:errorQualifies,
|
||||
logAttempts:true
|
||||
}
|
||||
|
||||
// mixin
|
||||
Object.keys(optionsDefault).forEach(function(k) {
|
||||
if (!options.hasOwnProperty(k)) {
|
||||
options[k] = optionsDefault[k];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// for recursion
|
||||
attempts = attempts || 1;
|
||||
|
||||
// make sure that the checker is really a function
|
||||
if (typeof(options.checker) !== "function") {
|
||||
throw ns.errorStack("if you specify a checker it must be a function");
|
||||
}
|
||||
|
||||
// check properly constructed
|
||||
if (!callBack || typeof(callBack) !== "function") {
|
||||
throw ns.errorStack("you need to specify a function for rateLimitBackoff to execute");
|
||||
}
|
||||
|
||||
function waitABit (theErr) {
|
||||
|
||||
//give up?
|
||||
if (attempts > options.maxAttempts) {
|
||||
throw errorStack(theErr + " (tried backing off " + (attempts-1) + " times");
|
||||
}
|
||||
else {
|
||||
// wait for some amount of time based on how many times we've tried plus a small random bit to avoid races
|
||||
Utilities.sleep (
|
||||
Math.pow(2,attempts)*options.sleepFor +
|
||||
Math.round(Math.random() * options.sleepFor)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// try to execute it
|
||||
try {
|
||||
var response = callBack(options, attempts);
|
||||
|
||||
// maybe not throw an error but is problem nevertheless
|
||||
if (options.lookahead && options.lookahead(response,attempts)) {
|
||||
if(options.logAttempts) {
|
||||
Logger.log("backoff lookahead:" + attempts);
|
||||
}
|
||||
waitABit('lookahead:');
|
||||
return ns.expBackoff ( callBack, options, attempts+1) ;
|
||||
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// there was an error
|
||||
catch(err) {
|
||||
|
||||
if(options.logAttempts) {
|
||||
Logger.log("backoff " + attempts + ":" +err);
|
||||
}
|
||||
|
||||
// failed due to rate limiting?
|
||||
if (options.checker(err)) {
|
||||
waitABit(err);
|
||||
return ns.expBackoff ( callBack, options, attempts+1) ;
|
||||
}
|
||||
else {
|
||||
// some other error
|
||||
throw ns.errorStack(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get the stack
|
||||
* @param {Error} e the error
|
||||
* @return {string} the stack trace
|
||||
*/
|
||||
ns.errorStack = function (e) {
|
||||
try {
|
||||
// throw a fake error
|
||||
throw new Error(); //x is undefined and will fail under use struct- ths will provoke an error so i can get the call stack
|
||||
}
|
||||
catch(err) {
|
||||
return 'Error:' + e + '\n' + err.stack.split('\n').slice(1).join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// default checker
|
||||
function errorQualifies (errorText) {
|
||||
|
||||
return ["Exception: Service invoked too many times",
|
||||
"Exception: Rate Limit Exceeded",
|
||||
"Exception: Quota Error: User Rate Limit Exceeded",
|
||||
"Service error:",
|
||||
"Exception: Service error:",
|
||||
"Exception: User rate limit exceeded",
|
||||
"Exception: Internal error. Please try again.",
|
||||
"Exception: Cannot execute AddColumn because another task",
|
||||
"Service invoked too many times in a short time:",
|
||||
"Exception: Internal error.",
|
||||
"User Rate Limit Exceeded",
|
||||
"Exception: ???????? ?????: DriveApp.",
|
||||
"Exception: Address unavailable"
|
||||
]
|
||||
.some(function(e){
|
||||
return errorText.toString().slice(0,e.length) == e ;
|
||||
}) ;
|
||||
|
||||
};
|
||||
|
||||
|
||||
return ns;
|
||||
})(Utils || {});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue