Photo by Dominik Dancs on Unsplash
As I am certain you are aware, this is the month of Hacktoberfest. I wanted to display statistics about my colleagues participation to the event and display it to our website. To achieve it I needed to use GitHub API I choosed to store my query in a Google Cloud Function. It was the occasion to test this service aswell.
While developping, I came across an issue. The Github API is quite long to respond nearly 2 seconds. Sometimes it takes even longer, I had a query lasting more than 10 seconds. Nobody want to wait for 10 seconds to browse a website. So I decided to add cache to my Cloud Function.
How does a GCP Cloud Function works?
While searching information about caching data I came across this documentation. Here is a quick summary:
A Cloud Function isn't receated from scratch at each invocation. The execution environnement is preserved between invocations but it is not guaranted. You can use global variable to store results of heavy computations.
Sounds to be what we are looking for!
Let's practice!
The data fetching isn't interesting for what we want to demonstrate. Let's say, it is done by the function fetchGitHubStats
which returns a Promise
. Our Cloud Function fetches the statistics then return the result.
function fetchGitHubStats() { /* ... */ }
exports.hacktoberfest = async (req, res) => {
// Fetch statistics from GitHub
const data = await fetchGitHubStats()
res.status(200).send(data);
};
First of all, we initialize a global variable to store the cached data. It has two properties:
- Data to store fresh statistics from GitHub API
- A TTL
What is a TTL?
TTL is a acronym for Time To Live. It is a timestamp that determines how long a data is valid.
We initialize the values with and empty array for the data and the current date for the TTL.
function fetchGitHubStats() { /* ... */ }
// We declare a global variable to store cached data
const cache = {
data: [],
ttl: new Date(),
}
exports.hacktoberfest = async (req, res) => {
// ...
};
Each time we fetch for new statistics we store the data in our global variable. We also generate a TTL of one hour we store alongside the data.
// ...
exports.hacktoberfest = async (req, res) => {
// Fetch statistics from GitHub
const data = await fetchGitHubStats()
// Store fresh data in cache
cache.data = data
// Store a TTL for the data
const dateInOneHour = new Date()
dateInOneHour.setHours(dateInOneHour.getHours() + 1);
cache.ttl = dateInOneHour
res.status(200).send(data);
};
Finally, at the beginning of our Cloud Function's handler, we check if the TTL of the cached data is still valid. If so, we return the data stored in cache.
// ...
exports.hacktoberfest = async (req, res) => {
// We check if our data was fetched more than an hour ago. It not we return the cached data
if (cache.data.length > 0 && cache.ttl > new Date()) {
return res.status(200).send(cache.data);
}
// ...
};
Here is the final result:
function fetchGitHubStats() { /* ... */ }
// We declare a global variable to store cached data
const cache = {
data: [],
ttl: new Date(),
}
exports.hacktoberfest = async (req, res) => {
// We check if our data was fetched more than an hour ago. It not we return the cached data
if (cache.data.length > 0 && cache.ttl > new Date()) {
return res.status(200).send(cache.data);
}
// Fetch statistics from GitHub
const data = await fetchGitHubStats()
// Store fresh data in cache
cache.data = data
// Store a TTL for the data
const dateInOneHour = new Date()
dateInOneHour.setHours(dateInOneHour.getHours() + 1);
cache.ttl = dateInOneHour
res.status(200).send(data);
};
Results
Cloud Function comes with nice graphs to visualize useful statistics about you function's life. Here is a first graph that shows us our function's invocations.
A second graph displays the execution time of our function. We clearly see our function is longer to execute each time it fetches fresh data.
It works 🎉
What we learned
It is time to check what we learned:
- Google Cloud Functions reuse execution environnement between invocation (not guaranted).
- You can use global variables to do heavy computations
- A simple global variable to store data with a TTL enables a powerful optimization
Hope it will you optimize your Cloud Functions.
Feedback is appreciated 🙏 Please tweet me if you have any questions @YvonnickFrin!
Top comments (2)
Nice solution.
It seems you have a typo in this code:
const dateInOneHour = new Date()
ttl.setHours(ttl.getHours() + 1);
cache.ttl = dateInOneHour
Should not it be:
const dateInOneHour = new Date()
dateInOneHour.setHours(dateInOneHour.getHours() + 1);
cache.ttl = dateInOneHour
Thank you Alex! I corrected it 👍