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