3 votes

Symfony 3.1 Cache - deleteItem ne fonctionne pas en prod

J'ai utilisé Symfony 3.1 avec le nouveau composant Cache ( https://symfony.com/doc/current/components/cache.html ), j'utilise l'adaptateur redis

config.yml

cache:
    app: cache.adapter.redis
    default_redis_provider: "redis://127.0.0.1:6379"

En gros, je sauvegarde des données dans redis lorsque je fais un GET vers une ressource spécifique, et je les supprime de redis, lorsque je fais un POST..

Avec Symfony en mode développement, les données sont stockées/supprimées du cache comme prévu. Mais quand je le mets en prod, le 'deleteItem' ne supprime plus l'élément du cache redis.. Je ne trouve aucune erreur dans les logs, donc je commence à être un peu perdu avec ça..

Voici un exemple de comment j'utilise le cache

protected function getCache(){
   return $this->get('cache.app');
}

public function getAction(){        
    $cacheItem = $this->getCache()->getItem('example-key');
    $data = ... // Vérifier si cacheItem isHit() ...      
    $cacheItem->expiresAfter($this->defaultCacheTime);
    $cacheItem->set($data);
    $this->getCache()->save($cacheItem);        
}

public function postAction() {
    ...
    $this->getCache()->deleteItem('example-key');
}

Mise à jour - J'ai trouvé ce qui pourrait causer ce problème

Voici une partie du code de AbstractAdapter de Symfony et de RedisAdapter:

public function deleteItem($key)
{
    return $this->deleteItems(array($key));
}

public function deleteItems(array $keys)
{
    $ids = array();

    foreach ($keys as $key) {
        $ids[$key] = $this->getId($key);
        unset($this->deferred[$key]);
    }

    try {
        if ($this->doDelete($ids)) {
            return true;
        }
    } catch (\Exception $e) {
    }

    $ok = true;

    // Lorsque la suppression en masse a échoué, réessayer chaque élément individuellement
    foreach ($ids as $key => $id) {
        try {
            $e = null;
            if ($this->doDelete(array($id))) {
                continue;
            }
        } catch (\Exception $e) {
        }
        CacheItem::log($this->logger, 'Échec de la suppression de la clé "{key}"', array('key' => $key, 'exception' => $e));
        $ok = false;
    }

    return $ok;
}

protected function doDelete(array $ids)
{
    if ($ids) {
        $this->redis->del($ids);
    }

    return true;
}

Voici une partie du code de Predis StreamConnection.php:

public function writeRequest(CommandInterface $command)
{
    $commandID = $command->getId();
    $arguments = $command->getArguments();

    $cmdlen = strlen($commandID);
    $reqlen = count($arguments) + 1;

    $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";

    for ($i = 0, $reqlen--; $i < $reqlen; $i++) {
        $argument = $arguments[$i];
        $arglen = strlen($argument);
        $buffer .= "\${$arglen}\r\n{$argument}\r\n";
    }

    $this->write($buffer);
}   

Quand j'appelle deleteItem('example-key'), cela appelle ensuite deleteItems(..) pour supprimer cette clé..

Le problème est que deleteItems() appelle doDelete() et passe un tableau de la forme 'example-key' => 'prefix_example-key'

Le doDelete() appelle ensuite le client Redis, en lui passant le même tableau string => string, alors que je pense qu'il devrait être, index => string, par ex: [0] => 'prefix_example-key' au lieu de ['example-key'] => 'prefix_example-key'

Ensuite, le client redis, lors du traitement de la commande à exécuter, reçoit ce tableau en tant qu'$arguments, et dans la boucle for, il fait ceci: $argument = $arguments[$i]; comme le tableau est au format string => string, cela ne fonctionnera pas, en mode développement, il affiche une erreur Notice undefined offset 0

C'est la partie étrange

  • En mode 'dev', il lancera une erreur, et donc, deleteItems() la capturera, et essayera de supprimer l'élément à nouveau, cette fois en envoyant correctement les arguments
  • En mode 'prod', la Notice undefined offset 0 je ne sais pas pourquoi, mais elle ne lève pas d'exception, donc deleteItems(..) ne la capture pas, et retourne à ce moment-là..

J'ai trouvé un moyen de le faire fonctionner pour moi, si j'ajoute array_values dans la méthode doDelete, cela fonctionne:

protected function doDelete(array $ids)
{
    if ($ids) {
        $this->redis->del(array_values($ids));
    }

    return true;
}

Je ne sais pas si tout cela a du sens ou non, je pense que je vais ouvrir un problème dans le suivi des bugs de Symfony

0voto

BraCa Points 116

Désolé, cela a été causé par une version obsolète de Predis, je pensais avoir la dernière version de Predis, mais ce n'était pas le cas

Tout fonctionne correctement avec la dernière version

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X