Skip to content

sutton-dev/query-cache

Repository files navigation

SOQL Query Cache

A high-performance, production-ready caching layer for Salesforce SOQL queries with intelligent query normalization, flexible storage strategies, and support for parameterized stored procedures.

Salesforce API License

🎯 Why Use SOQL Query Cache?

The Problem

In Salesforce, identical queries executed multiple times waste resources:

  • Governor limits consumed - Each Database.query() call counts toward your 100 SOQL limit
  • Slower performance - Repeated queries in Lightning components, service layers, and API endpoints
  • Poor UX - Dashboard/component reloads re-fetch the same data
  • Unnecessary database load - Same queries hit the database every time

Common scenarios:

  • Triggers - Multiple helper methods querying the same RecordTypes or Custom Metadata
  • Lightning components - Refreshing the same data multiple times per page load
  • Service layers - Methods called repeatedly with same parameters in one transaction
  • API endpoints - Serving the same data to multiple concurrent users
  • Dashboards - Aggregate queries that don't need real-time updates

The Solution

SOQL Query Cache provides intelligent caching for repeated queries:

  • Reduces SOQL governor limit usage - Cache hits don't count toward your 100 query limit
  • Saves time on cache hits - Typically 5-10ms per cached query vs 15-100ms database queries
  • Drop-in replacement for Database.query() - minimal code changes
  • Intelligent normalization - query variations automatically hit the same cache
  • Flexible storage - transaction-scoped, Platform Cache, or hybrid two-tier
  • Respects security - supports both with/without sharing modes

Note: Performance benefits vary significantly based on query complexity, data volume, and caching strategy. Run the included demos in your org to measure actual impact.


🚀 Quick Start

1. Installation

Deploy to your org:

sf project deploy start --source-dir force-app

2. Setup Platform Cache (Optional but Recommended)

For persistent caching across requests:

  1. Navigate to Setup → Platform Cache
  2. Create a new partition named SOQLCache
  3. Allocate 5-10 MB of cache space

3. Basic Usage

Replace Database.query() with SOQLQueryCache.query():

// Before
List<Account> accounts = Database.query('SELECT Id, Name FROM Account WHERE Industry = \'Technology\'');

// After
List<Account> accounts = (List<Account>)SOQLQueryCache.query('SELECT Id, Name FROM Account WHERE Industry = \'Technology\'');

That's it! Your queries are now cached automatically.

4. Run the Demo

Measure actual performance in your org:

SOQLCachePOC.quickDemo();  // Quick comparison demo
SOQLCachePOC.runDemo();    // Full demonstration suite

Important: Performance varies by query complexity, data volume, and org configuration. The demos show actual timings in your environment.


📊 Performance Expectations

What to Expect

Cache Hit Performance:

  • Cache lookup: <1ms (transaction cache) or 1-2ms (Platform Cache)
  • Normalization overhead: 1-2ms (simple queries) to 10-15ms (complex with subqueries)
  • Net benefit: Saves 5-10ms per cache hit for typical queries

Cache Miss (First Call):

  • Normalization: 1-15ms (depending on query complexity)
  • Database query: Variable (5ms to 100ms+ depending on data)
  • Cache storage: <1ms
  • Total: Slightly slower than Database.query() due to normalization overhead

Governor Limit Savings:

  • Cache hits don't count toward SOQL query limit (100 per transaction)
  • Actual reduction depends on your hit rate (typically 40-80%)

Expected Hit Rates

Good scenarios (60-80%+ hit rate):

  • Trigger helper methods querying same RecordTypes/Custom Metadata
  • Service layer methods called repeatedly in one transaction
  • Lightning components with multiple refresh cycles
  • API endpoints serving same data to multiple concurrent users

Poor scenarios (<30% hit rate):

  • Highly dynamic queries with many parameter variations
  • One-time queries
  • Queries with user-specific WHERE clauses (without Platform Cache)

Performance Notes

⚠️ Very fast queries (<10ms): May not benefit from caching due to normalization overhead ✅ Expensive queries (>30ms): Good candidates - normalization overhead is relatively small ✅ Repeated queries: Benefits multiply with each cache hit in the same transaction


🎯 Core Features

1. Transparent Caching

No query rewriting needed - queries are automatically cached and retrieved:

// First call - executes query and caches result
List<Account> accounts = (List<Account>)SOQLQueryCache.query(
    'SELECT Id, Name FROM Account WHERE Industry = \'Technology\''
);

// Second call - returns cached result instantly
List<Account> cached = (List<Account>)SOQLQueryCache.query(
    'SELECT Id, Name FROM Account WHERE Industry = \'Technology\''
);

2. Flexible Storage Strategies

Choose the right caching strategy for your use case:

Transaction-Only Cache (Default)

Fast, automatically cleared at end of request:

SOQLQueryCache.CacheOptions opts = new SOQLQueryCache.CacheOptions()
    .setStorage(SOQLQueryCache.CacheStorage.TRANSACTION_ONLY);

List<Account> accounts = (List<Account>)SOQLQueryCache.query(query, opts);

Platform Cache

Persists across requests and users with configurable TTL:

SOQLQueryCache.CacheOptions opts = new SOQLQueryCache.CacheOptions()
    .setStorage(SOQLQueryCache.CacheStorage.PLATFORM_CACHE)
    .setTTL(300); // 5 minutes

List<Account> accounts = (List<Account>)SOQLQueryCache.query(query, opts);

Two-Tier Hybrid (Recommended for Production)

Best of both worlds - fast transaction cache with Platform Cache fallback:

SOQLQueryCache.CacheOptions opts = new SOQLQueryCache.CacheOptions()
    .setStorage(SOQLQueryCache.CacheStorage.BOTH)
    .setTTL(300); // 5 minutes

List<Account> accounts = (List<Account>)SOQLQueryCache.query(query, opts);

3. Intelligent Query Normalization

Different query variations automatically hit the same cache entry:

// All three queries use the SAME cache entry:
SOQLQueryCache.query('SELECT Id, Name FROM Account WHERE Status = \'Active\'');
SOQLQueryCache.query('SELECT Name, Id FROM Account WHERE Status = \'Active\''); // Different field order
SOQLQueryCache.query('SELECT  Id,  Name  FROM  Account  WHERE Status=\'Active\''); // Different spacing

How it works:

  • Fields are sorted alphabetically
  • Whitespace is normalized
  • AND conditions are reordered (OR conditions preserved)
  • Subqueries are recursively normalized
  • Fast path optimization for simple queries (10x faster normalization)

4. Stored Procedures

Define reusable, parameterized queries using Custom Metadata:

Create a Stored Procedure

  1. Navigate to Setup → Custom Metadata Types → SOQL Stored Procedure → Manage Records
  2. Create a new record:
Field Value
Developer Name Get_High_Value_Accounts
Query SELECT Id, Name, Industry, AnnualRevenue FROM Account WHERE AnnualRevenue > :minRevenue AND Industry = :industry ORDER BY AnnualRevenue DESC
Parameters ["minRevenue", "industry"]
Cache TTL 300 (5 minutes)
Cache Storage Both
Active
Max Results 1000
Enforce Sharing

Execute the Stored Procedure

// Execute with parameters
Map<String, Object> params = new Map<String, Object>{
    'industry' => 'Technology',
    'minRevenue' => 1000000
};
List<Account> accounts = (List<Account>)SOQLStoredProcedure.execute('Get_High_Value_Accounts', params);

Benefits:

  • Centralized query management
  • Parameter validation and sanitization
  • Consistent caching configuration
  • Easy to update without code deployment

5. Cache Statistics and Monitoring

Track cache effectiveness in real-time:

SOQLCacheStatistics stats = SOQLQueryCache.getStatistics();

System.debug('Total queries: ' + stats.getTotalQueries());
System.debug('Cache hits: ' + stats.getCacheHits());
System.debug('Cache misses: ' + stats.getCacheMisses());
System.debug('Hit rate: ' + stats.getHitRate() + '%');
System.debug('Transaction cache hits: ' + stats.getTransactionHits());
System.debug('Platform cache hits: ' + stats.getPlatformHits());

// One-line summary
System.debug(stats.getSummary());
// Output: Total: 100, Hits: 75, Misses: 25, Hit Rate: 75.0%, Transaction: 60, Platform: 15

📚 Real-World Use Cases

Lightning Web Components (LWC)

Problem: Every component render queries the database

@AuraEnabled(cacheable=true)
public static List<Account> getAccounts() {
    return Database.query('SELECT Id, Name, Industry FROM Account LIMIT 100');
}
// Every component load: 20-30ms

Solution: Cache with Platform Cache

@AuraEnabled(cacheable=true)
public static List<Account> getAccounts() {
    SOQLQueryCache.CacheOptions opts = new SOQLQueryCache.CacheOptions()
        .setStorage(SOQLQueryCache.CacheStorage.PLATFORM_CACHE)
        .setTTL(300); // 5 minutes

    return (List<Account>)SOQLQueryCache.query(
        'SELECT Id, Name, Industry FROM Account LIMIT 100',
        opts
    );
}
// First load: ~25ms (normalization + query) | Subsequent loads: ~2ms (cached)

REST API Endpoints

Problem: Every API call hits the database

@RestResource(urlMapping='/api/accounts/*')
global class AccountAPI {
    @HttpGet
    global static List<Account> getAccounts() {
        return Database.query('SELECT Id, Name FROM Account ORDER BY Name');
    }
}
// Every request: 25-50ms

Solution: Cache across API calls

@RestResource(urlMapping='/api/accounts/*')
global class AccountAPI {
    @HttpGet
    global static List<Account> getAccounts() {
        SOQLQueryCache.CacheOptions opts = new SOQLQueryCache.CacheOptions()
            .setStorage(SOQLQueryCache.CacheStorage.PLATFORM_CACHE)
            .setTTL(600); // 10 minutes

        return (List<Account>)SOQLQueryCache.query(
            'SELECT Id, Name FROM Account ORDER BY Name',
            opts
        );
    }
}
// First request: ~30ms | Cached requests: ~2ms (saves SOQL limit + time)

Trigger Helper Methods

Problem: Multiple trigger helper methods query the same reference data

trigger AccountTrigger on Account (before insert, before update) {
    AccountTriggerHelper.validateIndustry(Trigger.new);      // Queries RecordTypes
    AccountTriggerHelper.setDefaults(Trigger.new);           // Queries RecordTypes AGAIN
    AccountTriggerHelper.enrichData(Trigger.new);            // Queries Custom Metadata
    AccountTriggerHelper.calculateRiskScore(Trigger.new);    // Queries Custom Metadata AGAIN
}
// 4 helper methods = 4+ redundant queries per trigger execution

Solution: Cache reference data across helper methods

public class AccountTriggerHelper {
    private static final SOQLQueryCache.CacheOptions CACHE_OPTS = new SOQLQueryCache.CacheOptions()
        .setStorage(SOQLQueryCache.CacheStorage.TRANSACTION_ONLY);

    private static Map<String, RecordType> getRecordTypeMap() {
        List<RecordType> rts = (List<RecordType>)SOQLQueryCache.query(
            'SELECT Id, DeveloperName FROM RecordType WHERE SObjectType = \'Account\'',
            CACHE_OPTS
        );
        // First helper method: ~15ms | Subsequent: <1ms (cached)
        return new Map<String, RecordType>(/* convert to map */);
    }

    private static Map<String, Industry_Settings__mdt> getIndustrySettings() {
        List<Industry_Settings__mdt> settings = (List<Industry_Settings__mdt>)SOQLQueryCache.query(
            'SELECT DeveloperName, Risk_Level__c FROM Industry_Settings__mdt',
            CACHE_OPTS
        );
        // Cached across all helper methods
        return new Map<String, Industry_Settings__mdt>(/* convert to map */);
    }

    public static void validateIndustry(List<Account> accounts) {
        Map<String, RecordType> rtMap = getRecordTypeMap(); // First call: DB query
        // ... validation logic
    }

    public static void setDefaults(List<Account> accounts) {
        Map<String, RecordType> rtMap = getRecordTypeMap(); // Cached!
        // ... default logic
    }
}
// 4 helper methods = 2 queries total (instead of 4+)
// Transaction cache automatically cleared when trigger completes

Repeated Method Calls Within Transaction

Problem: Same service method called multiple times in one transaction

public class AccountService {
    public static List<Account> getActiveAccounts() {
        return Database.query('SELECT Id, Name, Industry FROM Account WHERE IsActive__c = true');
    }
}

// Called multiple times during transaction
List<Account> accounts1 = AccountService.getActiveAccounts(); // 20ms
List<Account> accounts2 = AccountService.getActiveAccounts(); // 20ms again!
List<Account> accounts3 = AccountService.getActiveAccounts(); // 20ms again!
// Total: 60ms + uses 3 SOQL query limits

Solution: Cache within transaction

public class AccountService {
    private static final SOQLQueryCache.CacheOptions CACHE_OPTS = new SOQLQueryCache.CacheOptions()
        .setStorage(SOQLQueryCache.CacheStorage.TRANSACTION_ONLY);

    public static List<Account> getActiveAccounts() {
        return (List<Account>)SOQLQueryCache.query(
            'SELECT Id, Name, Industry FROM Account WHERE IsActive__c = true',
            CACHE_OPTS
        );
    }
}

// Called multiple times - only first call hits database
List<Account> accounts1 = AccountService.getActiveAccounts(); // ~20ms (miss)
List<Account> accounts2 = AccountService.getActiveAccounts(); // <1ms (cached hit)
List<Account> accounts3 = AccountService.getActiveAccounts(); // <1ms (cached hit)
// Total: ~22ms vs 60ms uncached | Uses 1 SOQL query vs 3

Complex Dashboard Queries

Problem: Multiple expensive aggregate queries

public class DashboardController {
    public List<AggregateResult> getMetrics() {
        // 5 different aggregate queries, 30-50ms each = 150-250ms total
        List<AggregateResult> revenue = Database.query('SELECT SUM(Amount) FROM Opportunity...');
        List<AggregateResult> counts = Database.query('SELECT COUNT(Id) FROM Account...');
        // ... more queries
    }
}

Solution: Cache all dashboard queries

public class DashboardController {
    private static final SOQLQueryCache.CacheOptions CACHE_OPTS = new SOQLQueryCache.CacheOptions()
        .setStorage(SOQLQueryCache.CacheStorage.BOTH)
        .setTTL(300); // Refresh every 5 minutes

    public List<AggregateResult> getMetrics() {
        // First load: ~200ms | Subsequent loads: ~10ms (saves SOQL limits)
        List<AggregateResult> revenue = (List<AggregateResult>)SOQLQueryCache.query(
            'SELECT SUM(Amount) FROM Opportunity...',
            CACHE_OPTS
        );
        // ... more cached queries
    }
}

🔧 Advanced Configuration

Custom Metadata Fields

Field Type Description
Query__c Long Text Area (131,072) The SOQL query with :parameter placeholders
Parameters__c Long Text Area JSON array of parameter names: ["param1", "param2"]
Description__c Text Area (255) Human-readable description
Cache_TTL__c Number Time-to-live in seconds (0 = transaction only)
Cache_Storage__c Picklist Transaction Only, Platform Cache, Both
Active__c Checkbox Enable/disable the stored procedure
Max_Results__c Number Safety limit for result size
Enforce_Sharing__c Checkbox Respect sharing rules

Cache Options API

SOQLQueryCache.CacheOptions opts = new SOQLQueryCache.CacheOptions()
    .setStorage(SOQLQueryCache.CacheStorage.BOTH)      // Storage strategy
    .setTTL(300)                                       // 5 minutes TTL
    .setMaxResults(1000)                               // Max 1000 records
    .setEnforceSharing(true)                          // Respect sharing
    .setBypassCache(false);                           // Force cache bypass

List<SObject> results = SOQLQueryCache.query(soql, opts);

Cache Invalidation

// Clear specific query
SOQLQueryCache.clearQuery('SELECT Id FROM Account');

// Clear by normalized cache key
String cacheKey = SOQLNormalizer.normalize('SELECT Id FROM Account');
SOQLQueryCache.clearCacheKey(cacheKey);

// Clear stored procedure cache
SOQLStoredProcedure.clearProcedureCache('Get_Active_Accounts');

// Clear with specific parameters
Map<String, Object> params = new Map<String, Object>{'industry' => 'Technology'};
SOQLStoredProcedure.clearProcedureCache('Get_Active_Accounts', params);

// Clear entire transaction cache
SOQLQueryCache.clearTransactionCache();

// Reset statistics
SOQLQueryCache.resetStatistics();

⚠️ Best Practices

✅ Good Candidates for Caching

  • Trigger helper methods - RecordTypes, Custom Metadata queried by multiple helpers
  • Lightning component queries - Same data fetched multiple times per page load
  • Service layer methods - Called repeatedly within a transaction
  • API endpoints - Multiple users/requests querying same data
  • Reference data - Picklists, metadata, configuration tables
  • Dashboard aggregates - Expensive queries that don't need real-time updates
  • Lookup/reference tables - RecordTypes, custom metadata, static hierarchies
  • Read-heavy queries - Data that changes infrequently (hourly/daily)

❌ Poor Candidates for Caching

  • Real-time data - Stock prices, live feeds requiring instant updates
  • User-specific data without sharing - Risks data leakage across users
  • Truly one-time queries - Only executed once per transaction/session
  • High-volatility data - Records that change every few seconds
  • Queries in loops - Anti-pattern; refactor to use sets/maps instead
  • Standard Salesforce Reports/Dashboards - Already optimized by platform
  • Very fast queries (<5ms) - Normalization overhead may exceed benefit

🎯 Optimization Tips

  1. Start with Transaction Cache

    • No setup required
    • Automatic cleanup
    • Perfect for queries repeated in same request
  2. Use Platform Cache for Expensive Queries

    • Queries taking >50ms
    • Queries executed across multiple requests
    • Data that changes infrequently
  3. Set Appropriate TTLs

    • Reference data: 600-3600 seconds (10 min - 1 hour)
    • Dashboard data: 300-600 seconds (5-10 minutes)
    • API responses: 60-300 seconds (1-5 minutes)
  4. Monitor Cache Effectiveness

    • Aim for >60% hit rate for good ROI
    • If hit rate <30%, reconsider caching strategy
    • Use SOQLCacheStatistics to track performance
  5. Invalidate on Data Changes

    • Clear cache after DML operations on cached objects
    • Use triggers or service layer to maintain consistency
  6. Use Stored Procedures for Common Queries

    • Centralize query management
    • Easier to update and maintain
    • Consistent caching configuration

🧪 Testing

Run Unit Tests

# Run all Apex tests
sf apex run test --test-level RunLocalTests --result-format human

# Run specific test classes
sf apex run test --tests SOQLQueryCacheTest,SOQLNormalizerTest,SOQLStoredProcedureTest

Test Coverage

All classes include comprehensive test coverage:

  • SOQLQueryCacheTest - Core caching functionality
  • SOQLNormalizerTest - Query normalization logic
  • SOQLStoredProcedureTest - Stored procedure execution

Integration Testing

Deploy the demo package and run:

SOQLCachePOC.quickDemo();      // Quick performance comparison
SOQLCachePOC.runDemo();        // Full demonstration suite

📈 Performance Metrics

Cache Overhead

Operation Time
Simple query normalization 1-2ms
Complex query normalization (with subqueries) 10-15ms
Transaction cache lookup <0.5ms
Platform cache lookup 1-2ms
Cache storage <1ms

Memory Usage

Cache Type Memory per Query Result
Transaction Cache ~1-10 KB per 100 records
Platform Cache ~1-10 KB per 100 records

Governor Limits

Limit Impact
SOQL queries ✅ Reduced (cached queries don't count)
CPU time ⚠️ Small increase (normalization overhead: 1-15ms)
Heap size ⚠️ Small increase (transaction cache: ~100 entries max)
Platform Cache ⚠️ Uses org-wide allocation (configure 5-10 MB)

🏗️ Architecture

Components

  1. SOQLQueryCache - Main caching layer

    • Manages transaction and Platform Cache storage
    • Tracks statistics
    • Coordinates with normalizer and executor
  2. SOQLNormalizer - Query normalization engine

    • Fast path for simple queries (indexOf-based)
    • Slow path for complex queries with subqueries
    • Recursive normalization for nested queries
  3. SOQLQueryExecutor - Sharing enforcement

    • with sharing inner class for enforced sharing
    • without sharing inner class for system mode
    • Ensures proper security context
  4. SOQLStoredProcedure - Stored procedure executor

    • Loads procedures from Custom Metadata
    • Handles parameter binding and validation
    • Generates procedure-specific cache keys
  5. SOQLCacheStatistics - Metrics tracking

    • Monitors hit/miss rates
    • Tracks storage tier usage
    • Provides summary reporting

Data Flow

User Query
    ↓
SOQLQueryCache.query()
    ↓
SOQLNormalizer.normalize() → Cache Key
    ↓
Check Transaction Cache → HIT? Return Result
    ↓ (MISS)
Check Platform Cache → HIT? Return Result (promote to Transaction)
    ↓ (MISS)
Database.query() → Execute Query
    ↓
Store in Cache(s)
    ↓
Return Result

🔒 Security Considerations

Parameter Binding

Stored procedures use String.escapeSingleQuotes() to prevent SQL injection:

// Safe parameter binding
String paramValue = String.escapeSingleQuotes(userInput);

Sharing Enforcement

Control sharing rules via enforceSharing option:

// WITH SHARING (default) - Respects sharing rules
SOQLQueryCache.CacheOptions withSharingOpts = new SOQLQueryCache.CacheOptions()
    .setEnforceSharing(true); // Recommended for user-visible data

List<Account> accounts = (List<Account>)SOQLQueryCache.query(
    'SELECT Id, Name FROM Account',
    withSharingOpts
);

// WITHOUT SHARING - Bypasses sharing rules (use with caution)
SOQLQueryCache.CacheOptions withoutSharingOpts = new SOQLQueryCache.CacheOptions()
    .setEnforceSharing(false); // For system-level operations

List<Account> allAccounts = (List<Account>)SOQLQueryCache.query(
    'SELECT Id, Name FROM Account',
    withoutSharingOpts
);

How it works:

  • enforceSharing=true → Uses with sharing inner class
  • enforceSharing=false → Uses without sharing inner class
  • Both still respect user permissions (FLS/CRUD)
  • Only sharing rules differ

Important: When using Platform Cache with enforceSharing=false, ensure cache keys include user context to prevent data leakage:

// BAD - All users share the same cache key
String query = 'SELECT Id FROM Account';
SOQLQueryCache.query(query, withoutSharingOpts); // Risk: User A sees User B's cached results

// GOOD - Include user context in query for user-specific data
String query = 'SELECT Id FROM Account WHERE OwnerId = \'' + UserInfo.getUserId() + '\'';
SOQLQueryCache.query(query, withoutSharingOpts); // Safe: Cache key includes user ID

Platform Cache Access

Platform Cache is org-wide and shared across all users. Ensure:

  • Sensitive data uses transaction-only cache
  • User-specific data includes user ID in cache key
  • Sharing rules are enforced for multi-user data

🐛 Troubleshooting

Cache Not Working

Symptom: Same query always hits database

Solutions:

  1. Verify Platform Cache partition exists and has space allocated
  2. Check cache options are properly configured
  3. Ensure query is identical (use SOQLNormalizer.normalize() to debug)
  4. Review debug logs for cache errors

Low Hit Rate

Symptom: Hit rate <30%

Solutions:

  1. Queries may be too dynamic (too many unique variations)
  2. TTL may be too short for Platform Cache
  3. Transaction cache only works within same request
  4. Consider using Stored Procedures for parameterized queries

Platform Cache Errors

Symptom: Platform Cache put failed warnings

Solutions:

  1. Verify partition name is exactly SOQLCache
  2. Check partition has available space
  3. Ensure Platform Cache is enabled in org
  4. Review org limits: Setup → Platform Cache

Performance Not Improving

Symptom: Cached queries not faster

Solutions:

  1. First query will always be slow (cache miss)
  2. Normalization adds 1-15ms overhead
  3. Very fast queries (<5ms) may not benefit from caching
  4. Review statistics to verify cache hits are occurring

🗺️ Roadmap

Future enhancements under consideration:

  • Cache warming utilities
  • Automatic cache invalidation on DML
  • Query performance analyzer
  • Multi-level cache hierarchies
  • Cache size optimization algorithms
  • Redis/External cache support
  • GraphQL-style query batching

📦 Package Structure

force-app/
├── main/default/
│   ├── classes/
│   │   ├── SOQLQueryCache.cls           # Main caching layer
│   │   ├── SOQLNormalizer.cls           # Query normalization
│   │   ├── SOQLQueryExecutor.cls        # Sharing enforcement (with/without)
│   │   ├── SOQLStoredProcedure.cls      # Stored procedure executor
│   │   ├── SOQLCacheStatistics.cls      # Statistics tracking
│   │   ├── SOQLQueryCacheTest.cls       # Cache tests
│   │   ├── SOQLNormalizerTest.cls       # Normalizer tests
│   │   ├── SOQLQueryExecutorTest.cls    # Sharing enforcement tests
│   │   └── SOQLStoredProcedureTest.cls  # Stored procedure tests
│   ├── cachePartitions/
│   │   └── SOQLCache.cachePartition-meta.xml
│   └── objects/
│       └── SOQL_Stored_Procedure__mdt/  # Custom metadata definition
force-app-demo/
└── main/default/classes/
    └── SOQLCachePOC.cls                 # Demo and POC code

🤝 Contributing

Contributions are welcome! Areas for improvement:

  • Additional cache eviction strategies
  • Cache warming utilities
  • Query performance analyzer
  • Multi-tier cache hierarchies
  • Documentation improvements

📄 License

MIT License - Use freely in your projects!


💬 Support

  • Issues: Open an issue in this repository
  • Questions: Review the demo code in SOQLCachePOC.cls
  • Examples: Check test classes for additional usage patterns

🙏 Acknowledgments

Built with ❤️ for the Salesforce developer community.

Special thanks to all contributors and early adopters who provided feedback and testing.


Ready to reduce SOQL governor limit usage? Deploy today and measure the impact in your org!

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published