← Back to Platform & Technical
PLAT-012 Platform & Technical 18 min read For: Architects

Platform Cache: Understanding Org, Session, and Partition Caches

The difference between Org Cache, Session Cache, and Partition Cache, the API for reading and writing cache values from Apex, appropriate caching patterns, invalidation strategies, and the common mistakes that lead to stale data bugs.

VS

Vishal Sharma

Salesforce Architecture Specialist · Updated May 2026

What You'll Learn

The difference between Org Cache, Session Cache, and Partition Cache, the API for reading and writing cache values from Apex, appropriate caching patterns, invalidation strategies, and the common mistakes that lead to stale data bugs.

Why Platform Cache Exists

Salesforce governor limits mean that every SOQL query, every callout, and every computation counts against a per-transaction budget. Data that changes rarely — Custom Metadata configurations, product rate tables, org-wide settings, lookup picklist values — is queried repeatedly on every page load or trigger execution. Platform Cache lets you store this data in memory and skip the SOQL entirely on subsequent accesses.

The performance improvement is significant: a cached value returns in microseconds versus a SOQL query that takes 5-50 milliseconds. For high-volume orgs with hundreds of concurrent users, this difference matters for both user experience and Salesforce platform stability.

Insight Platform Cache is an in-memory key-value store backed by Salesforce infrastructure. Unlike Static Variables (which are transaction-scoped and reset on every request), cached values persist across requests until their TTL expires or the cache is explicitly invalidated. This persistence is what makes it useful for expensive lookups.

Three Cache Tiers: Org, Session, and Partition

Org Cache stores data shared across all users in the org. A cached value written by User A is readable by User B. This makes it suitable for global configuration data, shared lookup tables, and org-wide computed values. Org Cache has a maximum TTL of 48 hours and a default capacity of 10 MB (expandable via Platform Cache licence add-on).

Session Cache stores data scoped to a single user's Lightning session. Data written during one page load is available on the next page load for the same user, but other users cannot read it. This is appropriate for user-specific preferences, mid-flow state, or data that varies per user but is expensive to recompute.

Partition Cache is the namespace concept within each tier. A partition is a named allocation within the Org or Session cache, created in Setup. Partitions allow packages and application components to manage their own cache space without colliding with other components' keys. Default partition names resolve to local.

// Writing to Org Cache
Cache.OrgPartition orgPart = Cache.Org.getPartition('local.myConfig');
orgPart.put('productRateTable', rateTableData, 3600); // TTL: 1 hour

// Reading from Org Cache
Map<String,Decimal> rates =
  (Map<String,Decimal>) orgPart.get('productRateTable');
if (rates == null) {
  // Cache miss — reload from SOQL
  rates = loadRatesFromSoql();
  orgPart.put('productRateTable', rates, 3600);
}

// Writing to Session Cache
Cache.SessionPartition sessPart =
  Cache.Session.getPartition('local.userPrefs');
sessPart.put('dashboardLayout', layoutData, 1800);

// Checking cache capacity
Cache.OrgPartition p = Cache.Org.getPartition('local.myConfig');
Long used = p.getNumKeys();
Long capacity = p.getCapacity();
Key Point Cached values must implement the Cache.CacheBuilder interface OR be serialisable (primitive types, custom objects with no transient fields, standard Apex types). Classes with static variables, non-serialisable system types (HttpRequest, Database.QueryLocator), or inner classes with references to outer-class state cannot be cached.

The CacheBuilder Pattern

The Cache.CacheBuilder interface provides a cleaner cache-or-load pattern that avoids the repetitive null-check boilerplate. Implement the interface, and the Cache.Org.get() method handles the miss automatically by calling your builder.

// CacheBuilder pattern
public class ProductRateCacheBuilder implements Cache.CacheBuilder {
  public Object doLoad(String requestedKey) {
    // requestedKey is the key passed to Cache.Org.get()
    return [SELECT Id, Rate__c FROM ProductRate__c
            WHERE ProductCode__c = :requestedKey LIMIT 1];
  }
}

// Usage — automatically loads on cache miss
ProductRate__c rate =
  (ProductRate__c) Cache.Org.get(ProductRateCacheBuilder.class, 'PROD-001');

// No null check needed — CacheBuilder guarantees a value or throws

Invalidation Strategies

Cache invalidation is the classic "hard problem" — data in cache goes stale when the underlying SOQL result changes. Three patterns handle this:

  • TTL expiry: Set the TTL to match the maximum acceptable staleness. For a rate table that is updated weekly, a 24-hour TTL is appropriate. Simple but imprecise — you may serve stale data for up to TTL duration.
  • Trigger-based invalidation: Add a trigger on the source object that calls orgPart.remove('productRateTable') whenever the source data changes. The next cache read triggers a reload. Precise but adds trigger complexity.
  • Version key pattern: Store a version number alongside the data. Increment the version when data changes. Cache keys incorporate the version number — stale keys simply expire unused. Useful when multiple callers cache derived views of the same source data.
Warning Never cache data that is security-sensitive in Org Cache. Because Org Cache is shared across all users, a record that User A should not see could be read from cache if User B had previously cached it. Use Org Cache only for data that is identical for all users (configuration, rate tables, metadata). User-specific data belongs in Session Cache.

Cache in Managed Packages and Sandboxes

Platform Cache is cleared on sandbox refresh. Any warm-cache assumptions built into application code will fail immediately after a sandbox refresh. Design cache-miss paths to handle cold starts gracefully — the application must function correctly (slowly) when the cache is empty.

In managed packages, use namespaced partition names to avoid collision with customer org cache content. A partition named mypackage.config is isolated from the customer's local.config partition. Declare required partition sizes in the package's Feature Parameters so customers know what to allocate.

Tip Instrument your cache hit rate in production. Log a custom Platform Event or increment an Org-wide Custom Setting counter on cache misses. If your hit rate drops below 80%, investigate whether TTLs are too short or data is changing too frequently for caching to be effective. A cache miss rate above 40% means you are adding complexity without meaningful performance benefit.

Key Takeaways

  • Org Cache is shared across all users — suitable for global configuration data but never for user-specific or security-sensitive data.
  • Session Cache is user-scoped — suitable for user preferences and per-user computed state.
  • Partitions are named namespaces within each cache tier — use them to prevent key collisions between packages and application components.
  • The CacheBuilder interface eliminates null-check boilerplate by handling cache miss and reload automatically.
  • Org Cache is cleared on sandbox refresh — always code for cold-start (cache-empty) operation.
  • Monitor cache hit rates in production — below 80% means the caching strategy needs revision.

Check Your Understanding

1. You need to cache a product rate table that is identical for all users and updated once per day. Which cache tier is appropriate?

A. Org Cache with a 24-hour TTL
B. Session Cache — rate data is user-specific
C. Static variables — they persist across all transactions

2. What happens to Platform Cache content when a sandbox is refreshed?

A. Cache content is preserved and carries over to the refreshed sandbox
B. Cache is cleared — application code must handle cold-start operation correctly
C. Cache content migrates from production to the sandbox during the refresh process

3. Which approach provides the most precise cache invalidation when the source data changes?

A. Setting a very short TTL (e.g. 60 seconds) to minimise staleness window
B. Trigger-based invalidation that calls orgPart.remove() when the source record changes
C. Using the CacheBuilder pattern, which automatically invalidates stale data

Discussion & Feedback