Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Cache not cleared with some Redis client #50026

Closed
@Grafikart

Description

@Grafikart

Symfony version(s) affected

6.X

Description

On my application the cache was not cleared when using the cache:clear console command or clear() method on the CacheItemPoolInterface.

When inspecting the problem a bit deeper I noticed that Redis was only clearing the first 1000 keys. I was able to track down the problem to these lines https://github.com/symfony/symfony/blob/6.3/src/Symfony/Component/Cache/Traits/RedisTrait.php#L453-L467.
In my case $host was an instance of Redis5Proxy (it seems to be injected automatically by Symfony) that returns the list of key when scan is called.

// $keys when scan() is called on a PredisClient
$keys = [
    0 => '1234',
    1 => [
        'cache_key:1',
        'cache_key:2',
        'cache_key:3',
        'cache_key:4',
    ]
];

// $keys when scan() is called on a Redix5Proxy
$keys = [
    'cache_key:1',
    'cache_key:2',
    'cache_key:3',
    'cache_key:4',
]

Since the keys are not what is expected by the system, the doClear() method ends up cleaning only the first 1000 keys.

How to reproduce

To reproduce the problem you would need a redis runing, where you insert more than 1000 keys (I did it using the Cache)

In the cache.yaml

framework:
  cache:
    app: app.cache.adapter.redis
    prefix_seed: mysite
    default_redis_provider: '%env(resolve:REDIS_URL)%'
    pools:
      view_cache_pool:
        default_lifetime: '7 days'

services:
  app.cache.adapter.redis:
    parent: 'cache.adapter.redis'
    arguments:
      $defaultLifetime: 604800
    tags:
      - { name: 'cache.pool', namespace: 'site_cache' }

Then a simple controller should work

<?php

namespace App\Http\Admin\Controller;

use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Routing\Annotation\Route;

class CacheController extends BaseController
{
    #[Route(path: '/cache/clean', name: 'cache_clean', methods: ['GET'])]
    public function clean(CacheItemPoolInterface $cache)
    {
       for ($i = 0; $i < 3000; $i++) {
          $item = $cache->getItem("demo{$i}");
          $item->set("demo{$i}");
          $cache->save($item);
       }
       $cache->clear(); // Will only clear 1000 first items
    }
}

Possible Solution

I see 2 possible solution at the moment :

  • throw an error the host is Redis5Proxy or a class that doesn't work as expected
  • Handle the case where we have no cursor. We could delete all the keys until keys array is empty ? But I don't know if there are possible side effects (risk of infinite loop)

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions