Start tracking your progress
Trailhead Home
Trailhead Home

Walk Through a Sample Application and Discover Cache Diagnostics

Learning Objectives

After completing this unit, you’ll be able to:

  • Explain a pattern for storing and refreshing cached data.
  • Decide on the data structure to use for the cached value.
  • Diagnose your cache usage.

Sample Application Walkthrough

Let's take a look at a sample application that demonstrates how to use the org cache for storing and retrieving currency exchange rates. Exchange rates fluctuate during a day so this sample app doesn’t return real-time rates. The app just provides a daily snapshot of exchange rates. Because we’re not interested in accurate real-time values but only in daily values, caching the currency exchange rates is a good choice. When exchange rates are retrieved repeatedly, getting them from the cache saves a lot of time and improves the performance of the app.

Sample Application Overview

We based our exchange rates sample application on a Visualforce page and an Apex controller that contains the logic for fetching exchange rates. The first time this page is accessed, the rates are obtained through an API call to an external web service. Subsequent executions of this page return the rates from the cache as long as the rates are less than a day old. For each exchange rate, this page displays the base currency, the target currency for conversion, and the conversion rate. For illustration purposes, a small set of currencies is returned.

Visualforce page displays currency exchange rates

This example is the markup of the Visualforce page. This page is associated with the ExchangeRates Apex controller.

<apex:page controller="ExchangeRates" action="{!init}">
    
   <apex:pageBlock title="Rates">
      <apex:pageBlockTable value="{!Rates}" var="rate">
         <apex:column value="{!rate.Base_Currency__c}"/>
         <apex:column value="{!rate.To_Currency__c}"/>
         <apex:column value="{!rate.Rate__c }"/>
      </apex:pageBlockTable>
   </apex:pageBlock>
   
</apex:page>

Sample Apex Controller

Our sample Apex controller does the heavy lifting. It fetches exchange rates, stores them in Salesforce and in the cache, and retrieves rates from the cache. Here is a breakdown of the operations that the sample controller does, followed by the source code.

The first time the sample is run, the following operations occur.

  • The exchange rates are obtained from an API call to an external endpoint.
  • The result (in JSON format) returned from the API call is parsed and saved in Exchange_Rate__c sObjects in Salesforce.
  • The getCachedRates() method stores the array of Exchange_Rate__c sObjects in the org cache.

On subsequent executions of the sample:

  • The sample checks how fresh the stored data is. To this end, it performs a SOQL query to fetch the createdDate value of the first returned Exchange_Rate__c record.
  • If the date is older than one day, the exchange rates are obtained from the API call, as in the first execution.
  • If the date is newer than one day, the rates are taken from the org cache. If there is a cache miss, the rates are queried from Exchange_Rate__c sObjects and stored in the org cache.
Note

Note

The Apex controller uses a helper Apex class called RateLib, which is not shown here. This helper class contains methods to make the outgoing API call to an exchange rate service, parse the JSON result from the API call, and store Exchange_Rate__c records.

public class ExchangeRates {
    private String currencies = 'EUR,GBP,CAD,PLN,INR,AUD,SGD,CHF,MYR,JPY,CNY';
    public String getCurrencies() { return currencies;}
    public Exchange_Rate__c[] rates {get; set;}

    //                                                                          
    // Checks if the data is old and gets new data from an external web service 
    // through a callout. Calls getCachedRates() to manage the cache.           
    // 
    public void init() {
        // Let's query the latest data from Salesforce
        Exchange_Rate__c[] latestRecords = ([SELECT CreatedDate FROM Exchange_Rate__c 
                        WHERE Base_Currency__c =:RateLib.baseCurrencies 
                              AND forList__c = true 
                        ORDER BY CreatedDate DESC
                        LIMIT 1]);
        
        // If what we have in Salesforce is old, get fresh data from the API
        if ( latestRecords == null  
            || latestRecords.size() == 0 
            || latestRecords[0].CreatedDate.date() < Datetime.now().date()) {
            // Do API request and parse value out
            String tempString = RateLib.getLoadRate(currencies);
            Map<String, String> apiStrings = RateLib.getParseValues(
                tempString, currencies);
            
            // Let's store the data in Salesforce
            RateLib.saveRates(apiStrings);

            // Remove the cache key so it gets refreshed in getCachedRates()
            Cache.Org.remove('Rates');
        }
        // Call method to manage the cache
        rates = getCachedRates();
    }

    //                                                                          
    // Main method for managing the org cache.                                  
    // - Returns exchange rates (Rates key) from the org cache.                 
    // - Checks for a cache miss.                                               
    // - If there is a cache miss, returns exchange rates from Salesforce       
    //    through a SOQL query, and updates the cached value.                   
    //
    public Exchange_Rate__c[] getCachedRates() {
        // Get the cached value for key named Rates
        Exchange_Rate__c[] rates = (Exchange_Rate__c[])Cache.Org.get(
            RateLib.cacheName+'Rates');
        
        // Is it a cache miss? 
        if(rates == null) {
            // There was a cache miss so get the data via SOQL
            rates = [SELECT Id, Base_Currency__c, To_Currency__c, Rate__c 
                        FROM Exchange_Rate__c 
                        WHERE Base_Currency__c =:RateLib.baseCurrencies 
                              AND forList__c = true
                              AND CreatedDate = TODAY];
            // Reload the cache
            Cache.Org.put(RateLib.cacheName+'Rates', rates);
        }
        return rates;
    }
}

To download the source of the Exchange Rates sample and play with it in your Developer org, see the Resources section.

Best Practices for Cache Management

Pattern for Cache Storage

The ExchangeRates Apex class contains methods that encapsulate the logic of initializing and refreshing the cache. If the data is stale or is not found, the init() method retrieves new exchange rates through an API call, and then stores them in Salesforce. The getCachedRates() method manages the cache internally. If the cached value is not found, this method retrieves an array of rates from Salesforce and stores it in the cache.

Because our app uses external data, it fetches data from a web service through API calls. It also stores the data as Salesforce records as a backup for refreshing the cache. Apps that don’t use external data retrieve Salesforce records by using SOQL and cache it. In this case, the process of cache management is simpler and the cache method implementation is shorter. For example, if your app just uses local data from SOQL, you wouldn’t need the init() method but only the getCachedRates() method.

We recommend that you include all the logic for managing the cache in one method. That way, the cache is manipulated in only one place in your app. The central management of the cache reduces the chance of errors from accessing invalid cache (cache misses) or accidentally overwriting cached values.

Deciding What to Cache

This sample stores an array of sObjects in the cache. Is this approach the best choice of data structure to store? There are tradeoffs with every choice you make. Storing smaller pieces of data, like field values instead of entire sObjects, can reduce your cache usage size. But if you store less data in each key, you might need complex logic to rebuild the data and sObjects, which requires more processing time. And an sObject array stored in one key uses less cache space than the total size of individual sObjects stored in individual keys. Caching smaller items instead of a list of items worsens cache performance because of the overhead of serialization and cache commit time. For example, instead of storing a list of rates (referenced by the rates variable in this snippet):

Cache.Org.put('Rates', rates);

You can store the individual rates, each as a field with its own cache key, as follows.

Cache.Org.put('DollarToEuroRate', rateEUR);
Cache.Org.put('DollarToChineseYuan', rateCNY);
Cache.Org.put('DollarToJapaneseYen', rateJPY);
// etc.

The decision of what data structure to cache depends on what your app does with the data. For example, if the app converts currencies for the same base currency, store at least the exchange rate for each target currency. You could store each currency rate as an individual key. But to make it easier to display the rates on a Visualforce page, cache the data as a list of sObjects. sObjects can increase storage space because they contain system fields, such as the created date, but they reduce logic processing time and increase app and cache performance. Remember that when using the cache, the main goal is to reduce the app’s execution time.

Diagnose Cache Usage

So, you’ve done all this work to implement Platform Cache. How do you know if you’re making the best use of the cache? There are a couple ways you can check performance data. One way is to view the diagnostics information in Setup (available in Salesforce Classic only).

Before you access the diagnostics page, enable the cache diagnostics permission for your user.

  1. From Setup, enter users in the Quick Find box, then select Users.
  2. Click your user’s name, and then click Edit.
  3. Select Cache Diagnostics, and then click Save.

Next, access the diagnostics page for a specific cache type in a partition.

  1. From Setup, enter cache in the Quick Find box, then select Platform Cache.
  2. Click the partition for which you want to check diagnostics information.
  3. Under session cache or org cache, click Diagnostics.

    Partition page contains links to the diagnostics pages for each cache type

If you click Diagnostics for the org cache, the Org Cache Diagnostics page opens in a new tab. This page shows two charts. The first chart (Org Cache Capacity and Usage) shows your cache usage limit. In our case, we’re way below the limit at 0.02%. The second chart (By Content Contribution) is a donut chart that shows cache distribution by key.

Note

Note

Because our sample uses the org cache, we’re inspecting the diagnostics page for only the org cache. A similar diagnostics page is also provided for the session cache for apps that use the session cache.

The following image shows a content distribution chart with four cache keys. One of the cache keys, Rates, is used in our exchange rates sample. The Rates key uses up more than half of the space.

Diagnostics page for org cache

After the charts, details of cache keys are listed. This list provides the size of each cached value corresponding to a key and the number of times the cache was accessed. You can delete a cache manually from this list as an alternative to calling the remove() method in Apex. The Delete button lets admins manage the cache without modifying any code.

The DollarToEuroRate key uses much less space than the Rates key. That’s to be expected because the DollarToEuroRate stores only one value while Rates stores an array of sObjects.

Use the information on the diagnostics page to determine whether to adjust your cache usage. For example, if a cache value has a low access count, indicating that it’s rarely used, consider whether it’s really needed, especially if it’s large. Or if you’re getting close to the cache usage limit, reconsider what to cache. You might want to remove unnecessary cache values or purchase more cache capacity.

Congratulations! You’ve learned how to use Platform Cache to cache your app’s data for fast retrieval. You’ve also learned about caching best practices and how to diagnose cache usage. You’re now ready to join the adventures of the chipmunk and cache precious resources!

retargeting