swayok / alternative-laravel-cache

Replacements for Laravel's redis and file cache stores that properly implement tagging idea
MIT License
169 stars 26 forks source link

Cache\Adapter\Common\Exception\CachePoolException: Exception thrown when executing "Cache\Adapter\Common\{closure}". #18

Closed waylaidwanderer closed 3 years ago

waylaidwanderer commented 4 years ago

This exception is happening often in /vendor/cache/adapter-common/AbstractCachePool.php:337:

Could it have something to do with the cached item being expired? TTL is set to 1800s in app/cache.php.

Backtrace

swayok commented 4 years ago

Hi. Exception thrown from core lib (it is not mine). My guess is that this happens because of cache store you are using. Something that cannot be treated as normal behavior. Perhaps if you're using file-based store - file may be already deleted or locked from reading or writing. Also I haven't experienced such problems myself while this lib is used in several production projects. I too use short TTLs. But I'm using latest redis stores. Let's start from your cache store settings.

waylaidwanderer commented 4 years ago

Isn't the error being triggered from the AlternativeCacheStore->decodeItem according to the backtrace? I didn't change any of the code other than installing your package with composer, and this error did not occur prior to that.

We're using redis (predis) for the cache store. Here's the cache.php config:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Default Cache Store
    |--------------------------------------------------------------------------
    |
    | This option controls the default cache connection that gets used while
    | using this caching library. This connection is used when another is
    | not explicitly specified when executing a given caching function.
    |
    | Supported: "apc", "array", "database", "file", "memcached", "redis"
    |
    */

    'default' => env('CACHE_DRIVER', 'file'),

    /*
    |--------------------------------------------------------------------------
    | Cache Stores
    |--------------------------------------------------------------------------
    |
    | Here you may define all of the cache "stores" for your application as
    | well as their drivers. You may even define multiple stores for the
    | same cache driver to group types of items stored in your caches.
    |
    */

    'stores' => [

        'apc' => [
            'driver' => 'apc',
        ],

        'array' => [
            'driver' => 'array',
        ],

        'database' => [
            'driver' => 'database',
            'table' => 'cache',
            'connection' => null,
        ],

        'file' => [
            'driver' => 'file',
            'path' => storage_path('framework/cache'),
        ],

        'memcached' => [
            'driver' => 'memcached',
            'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
            'sasl' => [
                env('MEMCACHED_USERNAME'),
                env('MEMCACHED_PASSWORD'),
            ],
            'options' => [
                // Memcached::OPT_CONNECT_TIMEOUT  => 2000,
            ],
            'servers' => [
                [
                    'host' => env('MEMCACHED_HOST', '127.0.0.1'),
                    'port' => env('MEMCACHED_PORT', 11211),
                    'weight' => 100,
                ],
            ],
        ],

        'redis' => [
            'driver' => 'redis',
            'connection' => 'cache',
        ],

    ],

    /*
    |--------------------------------------------------------------------------
    | Cache Key Prefix
    |--------------------------------------------------------------------------
    |
    | When utilizing a RAM based store such as APC or Memcached, there might
    | be other applications utilizing the same cache. So, we'll specify a
    | value to get prefixed to all our keys so we can avoid collisions.
    |
    */

    'prefix' => 'laravel',

    'ttl' => 1800, // 30 min
];

Edit: Laravel version is on 5.8.*

swayok commented 4 years ago

Backtrace states that exception happened in initialize() method called from isHit() method. Both are outside of my wrapper. It failed to call $func() ($this->callable) there which seems like getting data from redis. Probably it failed to get value from redis and crashed. It's hard to understand what exactly happened without original exception message. I think taht it may be related to predis or Redis server. There are no built-in handlers for exceptions so it just crashes. Maybe Laravel's cache driver handles exceptions so app won't crush when there is an error getting data from cache store. Can you switch from predis to php_redis extension? Extension is much faster and more stable. Haven't got any problems with it for a year. Also you can try...catch this exception and treat it as 'no cache for key'.

waylaidwanderer commented 4 years ago

If you set a tagged cache item with expiry, it doesn't get remove from the tag list (permanent redis key) when it expires. Maybe this is causing the issue?

e.g. The key "77c85149113dc8400ac9f6d9523c8fd0127bd35b" with value X is stored into redis with an expiry of 1800s. This key is also added to the list "tag!|laravel|role_user". However, when "77c85149113dc8400ac9f6d9523c8fd0127bd35b" expires, it's still kept in the tag list.

swayok commented 4 years ago

Well. I don't know deeply how underlying libs work with tags. It is actually a question to http://www.php-cache.com/

waylaidwanderer commented 4 years ago

I did add a try-catch as you suggested as a workaround, which works okay.

swayok commented 4 years ago

Ok. Also if it is possible - try swithing to php_redis extension (it is quite easy - just configs changes). It seems like predis was deprecated and mostly recommended only for servers where you cannot install php_redis extension. It would be wise to switch to extension asap.