Tuesday, 17 December 2024

Exponential Backoff

The following Google Apps Script is designed to explore Exponential Backoff - a process by which when something fails you try again a set number of times whilst increasing the delay between each attempt, up to a certain point.

I needed this for a tool I built which adds Guests to a Calendar event from a Google Form submission. Whilst I was using ScriptLock to prevent simultaneous submissions, the code ran so fast that it would infrequently trip the Calendar API with the following error message "API call to calendar.events.patch failed with error: Rate Limit Exceeded".

By infrequently I mean a reported issue only once in 3,500 submissions over the course of 12 months. Enough however to take the opportunity to learn about Exponential Backoff and to squash that single instance.

Just a note that this is one way to implement it.

Sample Apps Script code for Exponential Backoff
Sample Apps Script code for Exponential Backoff


The Code

There are a few elements of the code I want to draw out specifically, such as 'count' which is a variable we create to determine how many times we should iterate through our 'while()' loop when retrying the failed process - set to 3 in this example. Then there is our 'backOff' variable which is what we use for 'Utilities.sleep()' to determine how long we want to delay the process by - set to 2 seconds in this example (the actual value it requires needs to be in milliseconds):
// how many times the 'while' loop should iterate through
var count = 3;

// the delay in how long the code should wait before retrying, in milliseconds
var backOff = 2000;  // 2 seconds

In order to generate the specific error message I receive when updating Google Calendar events quicker than the API allows, I use the 'Math.random()' JavaScript method to generate a random number that I then test against a particular value. In this example by using '0.5' I create a 50% chance of the error message being shown via 'throw new Error'. You could tweak this to '0.9' for instance to create a 90% chance of seeing the error message if you require for your testing:
if (Math.random() < 0.5) {

    throw new Error("API call to calendar.events.patch failed with error: Rate Limit Exceeded");

};
We wrap the error in a try/catch to then allow us to perform a JavaScript 'match' on it to check it's the actual error message we are expecting. You can read about filtering a try/catch error message here. If we find a match in the error message we need to decrement our 'count' variable by 1 to acknowledge we've gone through the process once. We then need to check the value of this variable to determine if it has reached 0 - in which case we want to 'throw' the error message back to our Parent Function as we have reached the limit on how many times we wanted the while() loop to occur. Or we want our code implement the pause ('backOff') to slow it down, remembering to double the length of this afterwards (for instance 2 seconds to 4 seconds to 8 seconds ... etc):

// decrement count by 1;
count -= 1;

// if count is 0 then number of iterations has been reached
if (count == 0) {

    // return error message to 'catch' of Parent Function
    throw (error);

} else {

    // pause code for backoff period before trying again
    Utilities.sleep(backOff);

    // double length of backoff period each time
    backOff *= 2;

};


Download

Exponential Backoff download (please use 'Overview' > 'Make a copy' for your own version).

No comments:

Post a Comment