Salesforce Development: Apex

Salesforce Developer Tutorial – How and When to use the Platform Cache in Salesforce

4 min read

Subscribe on YouTube

Why This Is Useful

Remember from the static keyword episode that Apex statics only survive for a single transaction? That makes them useless as a real cache. The next request starts from nothing and you’re querying the same data all over again.

Platform Cache is the actual answer. It’s memory Salesforce sets aside for your org where you can stash data that survives between transactions and sessions.

If you’ve got reference data that barely changes and gets queried on every page load, this can meaningfully speed things up and save you SOQL queries.

Code’s on GitHub.


Two Flavours

Session Cache is tied to one user’s session. It dies when they log out, and it holds up to 8 hours. Use it for per-user stuff: their preferences, a wizard’s progress, their filter selections.

Org Cache is shared by everybody and isn’t tied to a session. Use it for data that’s the same for all users: country lists, exchange rates, configuration.

Getting this wrong is a genuine security problem. Put user-specific data in Org Cache and every user in your org can see it. When in doubt, ask: would I be comfortable if a different user got this exact value back?


Set Up A Partition First

You can’t use Platform Cache until you’ve allocated some. Go to Setup → Platform Cache, create a partition, and give it some session and org capacity.

Enterprise Edition and above get a free allocation. Developer Edition gets a small amount. If you see zero available, that’s why nothing’s working.


The Code

Apex
public with sharing class PartitionCacheExampleController
{
    @AuraEnabled
    public static void storeDataController()
    {
        Contact cont = [SELECT Id, FirstName, LastName, AccountId
                        FROM Contact LIMIT 1];

        //Default partition
        Cache.Session.put('ContactFound', cont);

        //Or name the partition explicitly
        Cache.Session.put('local.TacoCat.ContactFound', cont);
    }

    @AuraEnabled
    public static Contact retrieveDataCacheController()
    {
        return (Contact) Cache.Session.get('ContactFound');
    }

    @AuraEnabled
    public static Contact retrieveDataSOQLController()
    {
        return [SELECT Id, FirstName, LastName, AccountId
                FROM Contact LIMIT 1];
    }
}

Two methods doing the same job, one from cache and one from SOQL, so you can compare them directly. Put a timer around each and the difference is obvious.

That key format is worth understanding: local.TacoCat.ContactFound is namespace.partition.key. Use local when you don’t have a namespace. Skip the prefix entirely and it uses your default partition.

Note the cast on the way out. Everything comes back as Object, so you cast it to what you put in.


The Pattern You Should Actually Use

Here’s the thing that trips people up: cache is not guaranteed storage. Salesforce can evict your entry at any time if it needs the space. Entries also expire.

So never assume something’s there. Always write it like this:

Apex
public static List<Country__c> getCountries()
{
    List<Country__c> countries = (List<Country__c>) Cache.Org.get('Countries');

    if (countries == null)
    {
        //Cache miss. Fall back to the source of truth.
        countries = [SELECT Id, Name, Code__c FROM Country__c];

        //Store it for an hour
        Cache.Org.put('Countries', countries, 3600);
    }

    return countries;
}

Check the cache, fall back to SOQL on a miss, repopulate. Your code must work correctly even if the cache is completely empty every time. Cache is an optimisation, never a source of truth.

That third argument to put() is the time to live in seconds. Default is 8 hours for session, 24 for org. Set it deliberately based on how stale you can tolerate the data being.


What To Cache, And What Not To

Good candidates: reference data (countries, currencies, product catalogues), expensive aggregate calculations, results from a slow external API, configuration read on every page load.

Bad candidates: anything that changes frequently, anything where stale data causes real problems (prices, inventory, permissions), large volumes of data, and anything sensitive in Org Cache.

The honest test: if a user saw a version of this from ten minutes ago, would anything bad happen? If yes, don’t cache it.

Also worth saying: Custom Metadata is already cached by the platform and doesn’t count against SOQL limits. If your “reference data” is configuration, use Custom Metadata and skip this entirely.


Keeping It Fresh

If the underlying data changes, clear the cache. A trigger on the source object is the usual approach:

Code
Cache.Org.remove('Countries');

Simple, and it saves you fielding “why is it still showing the old value” questions.

Full details in the Platform Cache documentation.


Get Coding With The Force Merch!!

We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.

Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!


Check Out More Coding With The Force Stuff!

If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!

Youtube
Patreon
Github
Facebook
Twitter
Instagram


Salesforce Development Books I Recommend

Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps

Good Non-SF Specific Development Books:

Clean Code
Clean Architecture