Improving Mobile App load time: pre-compute data

Development dic 14, 2020

In the first post we talked about how we improved the speed of the endpoint used by our Customer App to load the notifications sent to a customer. Improving that endpoint was a quick fix but it did not solve completely our problems:

  • Too many calls to load the first screen shown to a logged in user
  • Some of the calls are slow (>= 1 sec)

Let’s analyze a little bit better the Customer App Home page. This is the Home page shown to a logged in user which did not make any purchase.

We need to show different data:

  1. Number of notifications not read
  2. Total money saved using Stamp
  3. The popular stores
  4. The best selling products

To load all this data we are doing around 9 calls:

  1. Get identity document (237 ms)
  2. Get payment methods (689 ms)
  3. Get residency (183 ms)
  4. Get total saved (222 ms)
  5. Get popular stores  (1089 ms)
  6. Get best selling products (1405 ms)
  7. Get brands (313 ms)
  8. Get categories (519 ms)
  9. Get total notifications not read (239 ms)

The first 2 calls are made in the UI thread and the others are executed in background, so the customer is not waiting too much to see some data on the screen, but still, for an user which just logged in the experience isn’t the best.

Which are our options?

We can improve the loading time of the Home in a couple of ways:

  1. Creating an endpoint which returns everything needed
  2. Pre-computing the data needed

We do not need to go for one solution or another, we can also combine the two of them.

What did we do?

After analyzing the data needed to show the Home page we understood that some of the calls like 1), 2), 3) are made just to understand if the profile of the customer is complete or not. The actual data, so the information contained inside, for example, the identity document (fullname, birth date, …) are used only in the Profile page, which “only” 20% of our customers go to.

Considering that all our services publish events when something happens in the system and that most of the data we show to the customer on that screen can be slightly outdated we decided to pre-compute some of the data.

We introduced two types of pre-computation, one for the data specific to the customer, the other one for the data that is the same for all the customers.

We created an Azure Web Job which subscribe to all the events needed to create two json, one containing the data specific for the user:

  • Total money saved using Stamp
  • A field which tell us if the profile is complete
  • A little bit of data needed in the Profile page

And the other one containing the generic data for all the users:

  • The popular stores
  • The best selling products

We decided to store the data in Azure Storage, as blobs, because it’s pretty fast and cheap.

So now when we show the Home screen we need to:

  1. Get pre-computed data
  2. Get brands
  3. Get categories
  4. Get total notifications not read

The endpoint to get the pre-computed data does three things:

  • Get the pre-computed data for the user
  • Get the generic pre-computed data
  • Merge them

Home endpoint V1 (1454 ms)

The first version of our endpoint executes these steps:

  1. Check if the Azure Blob Container exists
  2. Check if the pre-computed json for the customer is there
  3. Open the stream to read the pre-computed json for the customer
  4. Check if the generic pre-computed json is there
  5. Open the stream to read the generic pre-computed json
  6. Merge the two json

In the image below there are the first 3 steps:

After deploying our code and running a load test we found out that the average request time was pretty bad...around 1.5 seconds.

Home endpoint V2 (977 ms)

Checking the requests made we found out that our endpoint was making 5 HEAD HTTP requests, 3 were made by us to check if the container and the blobs exist, and 2 by the Azure Storage SDK.

Seeing that we were pretty sure that the container was there and that the blob where there we decided to just remove the checks and just handle the StorageException.

After the deployment we ran another load test and the average load time improved, 977ms.

Home endpoint V3 (550 ms)

Still we were not happy, so we decided to run the two tasks to get the user pre-computed data and the generic pre-computed data in parallel. We got something similar (I’ve removed some logging)

With the new changes the average response time went down to 550ms.

Can we do anything else?

Yes, we can do much more. We could cache the generic pre-computed data, use a faster data format than JSON (like protobuf), etc but we will talk about it in another post.

Conclusion

We were able to replace 6 calls to our APIs which were hitting tables not structured for reading with one endpoint which just gets two json files and merges them.

If we consider the best case scenario where all the 6 calls were made in parallel, the user was waiting 1405ms (the slowest call) to see all the data on the screen. Now the user just needs to wait 550ms… and this time can be improved!


Stamp Team

Stamp connects retailers with international customers cutting out intermediary agencies. We believe that technology can make shopping tax free simple and free of charges.

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.