In Winter ’16 Salesforce released a feature named Platform Cache, a memory layer that provides faster performance and better reliability when caching Salesforce user’s session and org data. We can specify what to cache and for how long (Time to Live) without using custom objects and settings or overloading the Visualforce view state. By distributing cache space across various applications or operations, Platform Cache improves performance such that cache capacity cannot be stolen by other applications or processes.
Platform Cache
Platform Cache is a memory layer that stores Salesforce session and org data for future use. Applications run faster, when we use Platform Cache because they store reusable memory. They can access this data quickly and they don’t need to duplicate calculations and requests to the database on subsequent transactions.
Types of Platform Cache:
1.Org Cache
Org cache can store Org-wide data such that anyone in the Org can access it. Org cache is accessible across sessions, requests, and org users and profiles. For org cache, data can live up to 48 hours in the cache. By default, the time-to-live(TTL) value for org cache is 24 hours.
2.Session Cache
Session cache stores data that are tied to a user’s session such that other users in the Org cannot access this data. The maximum life of a session cache is 8 hours.
Creating Platform Cache Partition:
By default, our Developer org has 0 MB cache capacity. We can request a trial cache of 10 MB. To request a trial, click Your Name [Symbol] Setup [Symbol] Develop [Symbol] Platform Cache[Symbol] New Platform Cache Partition.
Requesting Trial Capacity for Platform Cache:

Provide a suitable Name, and Label for the partition.
Specifying available Cache capacity:

Check default partition checkbox to use your cache name directly in Apex codes, without the namespace prefix.
If the trial cache capacity is available, then specify the capacity for both session cache and org cache.
Note: Sum of both the session and org cache capacity should be equal to the total available cache capacity. (Minimum Capacity is 5 MB)
Store/Retrieve Data in Org Cache:
To store values in an Org Cache use put(<CacheNameKey>,<Value>); and to retrieve values from an Org Cache we can use get(<CacheNameKey>);
- // Get partition
- Cache.OrgPartition orgPart = Cache.Org.getPartition(‘local.CurrencyCache’);
- // Add cache value to the partition
- orgPart.put(‘DollarToEuroRate’, ’0.91′);
- // Retrieve cache value from the partition
- String cachedData = (String)orgPart.get(‘DollarToEuroRate’);
- System.assertEquals(‘0.91’, cachedData);
Store/Retrieve Data in Session Cache:
We can use the Cache.Session and Cache.SessionPartition classes to manage values in the session cache. For managing values in any partition, use the methods in the Cache.Session class. If managing cache values are in one partition, use the Cache.SessionPartition methods instead.
Using Cache.Session:
To store a value in the session cache, call the Cache.Session.put() method and provide a key and value. The key name is in the format namespace.partition.key. To retrieve, use the key in the cache.Session.get() method.
- String username = ‘DemoUser’;
- // Add a value to the cache
- Cache.Session.put(‘local.MyCache.usernameCache’, username);
- if (Cache.Session.contains(‘local.MyCache.usernameCache’)) {
- // Retrieve value from the cache
- String cachedData = (String)Cache.Session.get(‘local.MyCache.usernameCache’);
- System.assertEquals(username, cachedData);
- }
Using Cache.SessionPartition:
To store a value in the session cache, call the Cache.Session.put() method and provide a key and value. The key name is in the format namespace.partition.key.
- // Get partition
- Cache.SessionPartition sessionPart = Cache.Session.getPartition(‘local.CurrencyCache’);
- // Add cache value to the partition
- sessionPart.put(‘FavoriteCurrency’, ’JPY’);
- // Retrieve cache value from the partition
- String cachedRate = (String)sessionPart.get(‘FavoriteCurrency’);
Storing Callout responses in a Platform Cache:
Now we implement this Platform Cache in a useful scenario – storing weather data temporarily in a platform cache. When a user request for weather details for a city for the first time from Salesforce, a callout is sent to a web service to fetch the weather details and the data is stored in platform cache with a specific time-to-live, say 6 hours. When the user requests again for the same city within that 6 hours, data is fetched from Platform Cache rather than sending callouts to web service.
Apex Visual Page (FetchWeather_VF):
- <apex:page controller=”FetchWeatherController”>
- <apex:sectionHeader title=”Fetch Weather Report” subtitle=”Select City”/>
- <apex:form>
- <apex:selectList size=”1″ value=”{! selectedCity}”>
- <apex:selectOptions value=”{! allUScities}”></apex:selectOptions>
- </apex:selectList>
- <apex:commandButton action=”{! retrieveWeatherDetails}” value=”Get Weather”/>
- <apex:outputPanel rendered=”{! IF(combinedResultsList.size != 0, TRUE, FALSE)}”>
- <apex:pageBlock tabStyle=”Contact”>
- <apex:pageBlockSection title=”{! selectedCity}’s Current weather”>
- <h3>Temperature </h3><apex:outputText value=”{! combinedResultsList[0]} K” />
- <h3>Pressure </h3><apex:outputText value=”{! combinedResultsList[1]}” />
- <h3>Humidity </h3><apex:outputText value=”{! combinedResultsList[2]} %” />
- <h3>Min Temperature </h3><apex:outputText value=”{! combinedResultsList[3]} K” />
- <h3>Max Temperature </h3><apex:outputText value=”{! combinedResultsList[4]} K” />
- </apex:pageBlockSection>
- </apex:pageBlock>
- </apex:outputPanel>
- </apex:form>
- </apex:page>
Apex Controller Class (FetchWeatherController):
- public class FetchWeatherController
- {
- public String[] combinedResultsList { get; set; }
- public String selectedCity { get; set; }
- private static String cachename = ’WeatherCache’;
- private static String apiKey = ”;
- private static String endPointURL= ”;
- public Cache.SessionPartition thisSession;
- private Cache.SessionPartition getPartition()
- {
- if(thisSession == null)
- thisSession = Cache.Session.getPartition(cacheName);
- return thisSession;
- }
- public SelectOption[] allUScities { get
- {
- allUScities = new List<SelectOption>();
- for(US_Cities__c option : US_Cities__c.getAll().values())
- allUScities.add(new SelectOption(option.City_API__c,option.Name));
- return allUScities;
- } private set;}
- public void initializeAPI()
- {
- apiKey = ’XxxxxxxxxxxxxxxxxxxxxxxxxxxxxxX’;
- endPointURL = ’http://api.openweathermap.org/data/2.5/weather’;
- combinedResultsList = new List<String>();
- thisSession = getPartition();
- }
- public FetchWeatherController()
- {
- combinedResultsList = new List<String>();
- thisSession = getPartition();
- }
- public void retrieveWeatherDetails()
- {
- initializeAPI();
- endPointURL += ’?q=’+selectedCity;
- endPointURL += ’&APPID=’ + apiKey;
- if(thisSession.contains(selectedCity))
- {
- combinedResultsList.addAll((List<String>)thisSession.get(selectedCity));
- return;
- }
- else
- {
- // Instantiate a new http object
- Http h = new Http();
- // Instantiate a new HTTP request, specify the method (GET) as well as the endpoint
- HttpRequest req = new HttpRequest();
- req.setEndpoint(endPointURL);
- req.setMethod(‘GET’);
- // Send the request, and return a response
- HttpResponse res = h.send(req);
- System.debug(”+res.getBody()+’ Code: ’+res.getStatusCode());
- if (res.getStatusCode() == 200)
- {
- Map<String, Object> resultsMap = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
- Map<String, Object> mainResultsMap = (Map<String, Object>)(resultsMap.get(‘main’));
- combinedResultsList.add(String.valueOf(mainResultsMap.get(‘temp’)));
- combinedResultsList.add(String.valueOf(mainResultsMap.get(‘pressure’)));
- combinedResultsList.add(String.valueOf(mainResultsMap.get(‘humidity’)));
- combinedResultsList.add(String.valueOf(mainResultsMap.get(‘temp_min’)));
- combinedResultsList.add(String.valueOf(mainResultsMap.get(‘temp_max’)));
- thisSession.put(selectedCity,combinedResultsList, 21600);
- }
- if(combinedResultsList.isEmpty())
- {
- combinedResultsList = new List<String>{‘Data Not available’,’Data Not available’,’Data Not available’,’Data Not available’,’Data Not available’};
- }
- }
- }
- }
Notes:
Using Platform Cache, we can replace custom settings or custom metadata, and, we can set Time-to-Live (TTL) for each data we store, which is an added advantage.
In the above scenario, we have minimized usage of web callouts, by retrieving temporary data from platform cache rather than callouts.
Similarly, we can store sObjects, SOQL queries, or any other data types in Platform Cache.
We can diagnose the performance of using platform cache in the following option:
Click Your Name [Symbol] Setup [Symbol] Develop [Symbol] Platform Cache [Symbol] <Your Platform Cache Name> [Symbol] Diagnostics
Diagnosing Cache usage:

Limits of Platform Cache:
Cache Allocations by Edition:
Platform Cache is available for Enterprise Edition orgs and above. These are the following editions that come with some default cache space. We can increase number of cache for greater performance enhancements.
- Enterprise Edition (10 MB by default)
- Unlimited Edition (30 MB by default)
- Performance Edition (30 MB by default)
- Developer Edition (request Trial capacity for 10 MB, 0 MB by default)
LIMITS | SESSION CACHE | ORG CACHE |
Minimum partition size | 5 MB | 5 MB |
Maximum size of a single cached item | 100 KB | 100 KB |
Maximum local cache size for a partition, per-request | 500 KB | 1000 KB |
Minimum time-to-live | 300 seconds (5 minutes) | 300 seconds (5 minutes) |
Maximum time-to-live | 28,800 seconds (8 hours) | 172,800 seconds (48 hours) |
Default cache time-to-live | 28,800 seconds (8 hours) | 86,400 seconds (24 hours) |
Conclusion:
Platform Cache can increase performance, replace Custom Settings and Custom Meta Data Types by storing data temporarily without any need of extra storage, and eliminates the need to use web callouts (as per our scenario).
References: