How to speed up your Forge application using Storage API

How to speed up your Forge application using Storage API

This blog post talks about my and my team's experience participating in the Codegeist '21 Hackathon organised by Atlassian and our learnings. We also put together a tutorial on how we improved the performance of the Forge app we built.

Our Codegeist Experience

We came across the Codegeist hackathon from the Devpost hackathons page. This hackathon seemed different from the rest of the hackathons we had ever participated in before. The submission period duration being 2 months made it possible for students like us to manage both the hackathon and academic commitments well. Secondly, the hackathon required us to use the Forge Platform instead of building from scratch, something which we had rarely done before. Considering these and the fact that we would get to learn more about the Atlassian products, we decided to give this hackathon a try!

Initial Research

We weren’t exactly sure initially what and how we were going to build a product. So the majority of the time we spent trying various tools like Trello, Confluence, Jira. Simultaneously, we started looking into the possibilities of additions to the existing applications that would simplify/automate things from a user standpoint. Being students with a limited idea about the tools, it was important to understand the problems faced by the communities and different perspectives before we start building, so we browsed through marketplace apps, reviews and forums. Finally, we created a list of possible ideas that we could implement. Next, we set up the development environment and started experimenting with the Forge App.

Learnings

Now that the 2-month hackathon is complete, we would like to reflect on our key learnings and takeaways.

  • Forge Platform and its usage - From being clueless to building and distributing our own app. Although there is still a lot yet to explore in this regard, we are delighted with the progress we made this past month during the hackathon.

  • Planning is key - Spend enough time to think about a problem. To build an innovative and well-thought solution, extensive work is a need in the designing phase and this hackathon proves this well.

  • Virtual collaboration - With most of the communication being virtual in recent times, this hackathon gave us an opportunity to improve our remote collaboration and management. We also added the newly learnt Atlassian tools to our arsenal.

Finding a problem and developing a solution was challenging, but at the same time, a very interesting and rewarding experience. Our project 'Workspace Cleaner for Confluence' aims to help users easily declutter their workspaces by showing them the most inactive pages in their workspace. You can view the project here.

We would like to give a tutorial on how we sped up our Forge application using the Storage API.

Tutorial: How to speed up your Forge application using Storage API

Our app essentially shows the most inactive pages in their workspace. We calculated the activity of a page by taking into account various factors such as comments, last updated, total views, unique views, watchers, etc.,

For this, we’re first fetching a list of all the pages by making an API call to the endpoint: Get the list of pages in a space

Then, for each page we had to make API calls to the following endpoints:

This resulted in a very high response time. As the number of pages in space grows with time, the response time will also increase proportionally.

Let’s see how we can speed up our app step by step:

Part 0 - Set up

Clone the source code for the app at this link and follow the instructions given in the README.md

Install the app on a site where you have access to the admin panel. You will see an app named “Forge Tutorial (Development)” on the left sidebar. Click on it to run the app. Give the app the necessary permission to run.

Part 1 - Logging response times

To measure the exact response time, we tried logging the response times:

  1. Add the lines on line numbers 1 and 10 in App class in index.jsx:

    const [startTime, setStartTime] = useState(new Date());
    const [data, setData] = useState(fetchData);
    const [user, setUser] = useState(fetchCurrentUser);
    const [lastUpdatedOn, setLastUpdatedOn] = useState(fetchLastUpdatedOn);
    const [isOpen, setOpen] = useState(false);
    const [selectedPageIndex, setSelectedPageIndex] = useState(0);
    
    console.log("Response Time: " + ((new Date()) - startTime) + "ms")
    
  2. Deploy the app using forge deploy and run the app again.

You can see the logged response times using the command forge logs

The response times logged on 10 such app reloads are as follows:

$ forge logs
INFO    2021-09-22T08:28:41.406Z 1d3a21f2-1d2c-4ddc-9dae-d55d0b2157cc Response Time: 7978ms
INFO    2021-09-22T08:28:48.971Z a3c7b43e-f1dc-4ea9-a6c8-f867e6755fef Response Time: 7438ms
INFO    2021-09-22T08:29:03.291Z 15f02424-e2ce-4397-a227-f8a7a14bd862 Response Time: 6760ms
INFO    2021-09-22T08:29:10.259Z 8ab7cffe-b129-4df1-84ca-31377681d449 Response Time: 7198ms
INFO    2021-09-22T08:29:19.104Z 607ec2c7-7f15-45f5-b6f3-01476d6cabc1 Response Time: 6999ms
INFO    2021-09-22T08:29:29.365Z 80dfd42c-6ada-4150-8ada-cb8f15c4571b Response Time: 6482ms
INFO    2021-09-22T08:30:48.621Z d4cd8275-c41d-4953-9b3f-e4c3f5368a9a Response Time: 6341ms
INFO    2021-09-22T08:30:49.678Z d36496f0-189d-4566-bbc2-9b5690c1f462 Response Time: 6605ms
INFO    2021-09-22T08:31:03.751Z 6f131c9b-bfd5-4540-b3b4-2fdd58510cc4 Response Time: 6279ms
INFO    2021-09-22T08:31:03.751Z fccc79c7-5bb7-4dad-b9bc-e650f83e472e Response Time: 6127ms

The average response time was 6.821 seconds every time the page was loaded! Ideally, the response time should be around 1 second.

Part 2 - Add Caching

Since there is no need for real-time data in our app, we can cache the data fetched instead of fetching data for all the endpoints every time the app is reloaded.

Generally, caching is done using browser storage or using services such as Redis. But Forge provides a powerful storage API that stores data in the form of key-value pairs. The storage is common for a particular site i.e. different sites will have separate storage.

To add caching to the application, you can simply switch to the add-caching branch in the repo using:

$ git checkout add-caching

Or you can follow the steps below:

  1. Add storage:app permission scope to manifest.yml

- "storage:app"

  1. Rename fetchData() to refreshData() in index,jsx (line no. 88)
    const refreshData = async () => {
    const response = await api
     .asUser()
     .requestConfluence(
    ...
    
  2. Create a fatchData function that will check if the data is present in the cache(Storage API). If present, the data will directly be fetched from the cache. Else, we will fetch the data from the endpoints and save it in the cache.
const fetchData = async () => {
  let data = await storage.get("data");   // Fetch from Storage
  if (data) return data;

  result = await refreshData();
  await storage.set("data", result);  // Save in Storage
  return result;
};
  1. Deploy the app using forge deploy
  2. Run the app

    Results:

    INFO    2021-09-22T08:51:59.279Z 34b4baa1-9352-432e-81e8-cc18c61554e0 Response Time: 7656ms
    INFO    2021-09-22T08:54:19.308Z e70f4d32-f7b6-41cf-b030-4995cd5b78ea Response Time: 502ms
    INFO    2021-09-22T08:54:26.840Z 0550d35e-5fbf-4459-b31d-490f526634fd Response Time: 553ms
    INFO    2021-09-22T08:54:34.170Z 57b86203-a679-4b69-8e9c-aa0b8214e0ad Response Time: 484ms
    INFO    2021-09-22T08:54:39.961Z 73f71028-7fc2-4418-9550-66015325c092 Response Time: 474ms
    INFO    2021-09-22T08:54:45.869Z b85e611c-ead4-43ba-9c60-97dc6669c051 Response Time: 462ms
    INFO    2021-09-22T08:55:11.385Z 3823ad1a-1a15-4296-ac56-b6b49a7ef09b Response Time: 658ms
    INFO    2021-09-22T08:55:19.771Z 41530352-37f9-4263-991b-1e90823ebf30 Response Time: 426ms
    INFO    2021-09-22T08:55:22.504Z 24d0d2f0-b043-4a41-b0fe-f9d6dc581d18 Response Time: 479ms
    INFO    2021-09-22T08:55:28.348Z 5ca9d547-cb2c-412c-af80-049ac5d2471d Response Time: 462ms
    

As we can see only the response time initially is very large and then for consecutive reads, it’s directly fetching the data from the storage API. The average response time now becomes 1.2s.

This is just one way to speed up your Forge application. We would love to hear from you what techniques do you use to improve the performance of your applications.