DEV Community

Cover image for How to add cache to your GCP Cloud functions in JavaScript
🦁 Yvonnick FRIN
🦁 Yvonnick FRIN

Posted on • Updated on • Originally published at yvonnickfrin.dev

How to add cache to your GCP Cloud functions in JavaScript

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);
};

Enter fullscreen mode Exit fullscreen mode

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) => {
  // ... 
};

Enter fullscreen mode Exit fullscreen mode

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);
};

Enter fullscreen mode Exit fullscreen mode

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);
  }

  // ...
};

Enter fullscreen mode Exit fullscreen mode

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);
};

Enter fullscreen mode Exit fullscreen mode

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.

Cloud Function calls graph

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.

Cloud Function execution time graph

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)

Collapse
 
alex_elesus profile image
Alex Sevostjanov

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

Collapse
 
yvonnickfrin profile image
🦁 Yvonnick FRIN

Thank you Alex! I corrected it 👍