Search code examples
phplaravelloopsoopcaching

Best way to avoid reloading from cache repititively?


Consider having a Class whose method is being called in iteration n times, and the method in itself has a statement that pulls data from a cache system - Redis.

Now if this method is called n times the cache is also hit n times, leading to a continuous fetch and unserializing of the same data n times, which in the case of an interpreter like PHP consumes a good amount of time & CPU.

Passing the cached data to this method could also be a no, as we might instantiate n number of instances of this class.

Hence is there a way where we can avoid hitting the cache multiple times in the context of a Class and/or Object?

Maybe we can somehow use static properties of the Object to hold the value?


Solution

  • First, write a service class that:

    • Provides getter, which:
      • Loads required value from cache (based on unique-key),
      • But also backups the loaded-value,
      • Finally, returns backupped-value instead of re-loading from cache.
    • Provides setter which updates both backup and cache.

    Then simply use Laravel's feature to inject and/or get instance of said service-class.

    Example

    <?php
    
    namespace App\Services;
    
    use Illuminate\Support\Facades\Cache;
    
    class MyCacheService
    {
        protected $backupMap = [];
    
        /**
         * Requests an instance of this-class from Laravel. 
         *
         * @return MyCacheService|null
         */
        public static function instance()
        {
            return \Illuminate\Support\Facades\App::make(MyCacheService::class);
        }
    
        public function get($key)
        {
            $backup = & $this->backupMap[$key];
            if ( ! isset($backup)) {
                $backup = $this->rawGet($key);
            }
            return $buckup;
        }
    
        public function set($key, $value)
        {
            $this->rawSet($key, $value);
            $this->backupMap[$key] = $value;
        }
    
        /** Loads from cache */
        private function rawGet($key)
        {
            // ... your normal loading from cache logic goes here,
            // but as sub-example:
    
            return Cache::get($key);
        }
    
        /** Saves into cache */
        private function rawSet($key, $value)
        {
            // ... your normal saving into cache logic goes here,
            // but as sub-example:
            Cache::set($key, $value);
        }
    }
    

    Usage:

    use App\Services\MyCacheService;
    use Illuminate\Support\Facades\App;
    
    // ...
    
    // Get instance once, like:
    $cache = MyCacheService::instance();
    
    // Or like:
    /** @var MyCacheService|null $cache */
    $cache = App::make(MyCacheService::class);
    
    // Finally, reuse as many times as required, like:
    $myValue = $cache->get('my-unique-key');
    
    $myOtherValue = $cache->get('my-other-key');
    

    Note that Laravel stores the class's instance for us, else one could use private static property named $instance, and return that from instance() method.

    WARNING: as you may already know, maybe replace rawGet and rawSet's logic.

    Also, place MyCacheService.php file (which's source is above) in app/Services folder.