From 1b619b05abf53a3a070da5321d6fbb8012f2fe0e Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Thu, 19 Dec 2024 17:33:19 +0100 Subject: [PATCH 01/13] nuova versione per il cluster --- src/Console/CleanOrphanedKeysCommand.php | 91 +++++++++-- src/Console/GetClusterNodesCommand.php | 39 +++++ src/Console/ListenerCommand.php | 198 ++++++++++++++--------- src/RedisConnector.php | 72 ++++----- src/Service/GetClusterNodesService.php | 71 ++++++++ src/SuperCacheManager.php | 150 +++++++++++------ src/SuperCacheServiceProvider.php | 2 + tests/Unit/SuperCacheManagerTest.php | 16 +- 8 files changed, 458 insertions(+), 181 deletions(-) create mode 100644 src/Console/GetClusterNodesCommand.php create mode 100644 src/Service/GetClusterNodesService.php diff --git a/src/Console/CleanOrphanedKeysCommand.php b/src/Console/CleanOrphanedKeysCommand.php index a034e45..e566e5a 100644 --- a/src/Console/CleanOrphanedKeysCommand.php +++ b/src/Console/CleanOrphanedKeysCommand.php @@ -5,22 +5,92 @@ use Illuminate\Console\Command; use Illuminate\Support\Facades\Log; use Padosoft\SuperCache\RedisConnector; +use Padosoft\SuperCache\Service\GetClusterNodesService; +use Padosoft\SuperCache\SuperCacheManager; class CleanOrphanedKeysCommand extends Command { - protected $signature = 'supercache:clean-orphans {--connection_name= : (opzionale) nome della connessione redis}' ; + protected $signature = 'supercache:clean-orphans {--connection_name= : (opzionale) nome della connessione redis}'; protected $description = 'Clean orphaned cache keys'; protected RedisConnector $redis; protected int $numShards; + protected SuperCacheManager $superCache; + protected GetClusterNodesService $getClusterNodesService; - public function __construct(RedisConnector $redis) + public function __construct(RedisConnector $redis, SuperCacheManager $superCache, GetClusterNodesService $getClusterNodesService) { parent::__construct(); $this->redis = $redis; $this->numShards = (int) config('supercache.num_shards'); // Numero di shard configurato + $this->superCache = $superCache; + $this->getClusterNodesService = $getClusterNodesService; } public function handle(): void + { + if (config('database.redis.clusters.' . ($this->option('connection_name') ?? 'default')) !== null) { + $this->handleOnCluster(); + } else { + $this->handleOnStandalone(); + } + } + + public function handleOnCluster(): void + { + // Tenta di acquisire un lock globale + $lockAcquired = $this->superCache->lock('clean_orphans:lock', $this->option('connection_name'), 300); + if (!$lockAcquired) { + return; + } + // Purtroppo lo scan non funziona sul cluster per cui va eseguito su ogni nodo (master) + $array_nodi = $this->getClusterNodesService->getClusterNodes($this->option('connection_name')); + + foreach ($array_nodi as $node) { + [$host, $port] = explode(':', $node); + + // Per ogni shard vado a cercare i bussolotti (SET) dei tags che contengono le chiavi + for ($i = 0; $i < $this->numShards; $i++) { + // Inserisco nel pattern supercache: così sonop sicura di trovare solo i SET che riguardano il contesto della supercache + $shard_key = '*' . config('supercache.prefix') . 'tag:*:shard:' . $i; + // Creo una connessione persistente perchè considerando la durata dell'elaborazione si evita che dopo 30 secondi muoia tutto! + $connection = $this->redis->getNativeRedisConnection($this->option('connection_name'), $host, $port); + + $cursor = null; + do { + $response = $connection['connection']->scan($cursor, $shard_key); + + if ($response === false) { + //Nessuna chiave trovata ... + break; + } + + foreach ($response as $key) { + // Ho trovato un bussolotto che matcha, vado a recuperare i membri del SET + $members = $connection['connection']->sMembers($key); + foreach ($members as $member) { + // Controllo che la chiave sia morta, ma ATTENZIONE non posso usare la connessione che ho già perchè sono su un singolo nodo, mentre nel bussolotto potrebbero esserci chiavi in sharding diversi + if ($this->redis->getRedisConnection($this->option('connection_name'))->exists($member)) { + // La chiave è viva! vado avanti + continue; + } + // Altrimenti rimuovo i cadaveri dal bussolotto e dai tags + // Qui posso usare la connessione che già ho in quanto sto modificando il bussolotto che sicuramente è nello shard corretto del nodo + $connection['connection']->srem($key, $member); + // Rimuovo anche i tag, che però potrebbero essere in un altro nodo quindi uso una nuova connessione + $this->redis->getRedisConnection($this->option('connection_name'))->del($member . ':tags'); + } + } + } while ($cursor !== 0); // Continua fino a completamento + + // Chiudo la connessione + $connection['connection']->close(); + } + } + // Rilascio il lock globale + $this->superCache->unLock('clean_orphans:lock', $this->option('connection_name')); + } + + public function handleOnStandalone(): void { // Carica il prefisso di default per le chiavi $prefix = config('supercache.prefix'); @@ -33,7 +103,6 @@ public function handle(): void local success, result = pcall(function() local database_prefix = string.gsub(KEYS[5], "temp", "") local shard_prefix = KEYS[1] - local tags_prefix = KEYS[6] local num_shards = string.gsub(KEYS[2], database_prefix, "") local lock_key = KEYS[3] local lock_ttl = string.gsub(KEYS[4], database_prefix, "") @@ -41,8 +110,10 @@ public function handle(): void -- Tenta di acquisire un lock globale if redis.call("SET", lock_key, "1", "NX", "EX", lock_ttl) then -- Scansiona tutti gli shard + -- redis.log(redis.LOG_NOTICE, "Scansiona tutti gli shard: " .. num_shards) for i = 0, num_shards - 1 do local shard_key = shard_prefix .. ":" .. i + -- redis.log(redis.LOG_NOTICE, "shard_key: " .. shard_key) -- Ottieni tutte le chiavi dallo shard local cursor = "0" local keys = {} @@ -55,15 +126,17 @@ public function handle(): void until cursor == "0" -- Cerco tutte le chiavi associate a questa chiave for _, key in ipairs(keys) do + -- redis.log(redis.LOG_NOTICE, "CHIAVE: " .. key) local members = redis.call("SMEMBERS", key) for _, member in ipairs(members) do + redis.log(redis.LOG_NOTICE, "member: " .. database_prefix .. member) if redis.call("EXISTS", database_prefix .. member) == 0 then -- La chiave è orfana, rimuovila dallo shard redis.call("SREM", key, member) + -- redis.log(redis.LOG_NOTICE, "Rimossa chiave orfana key: " .. key .. " member: " .. member) -- Devo rimuovere anche la chiave con i tags - -- gescat_laravel_database_supercache:tags:supercache:trilly - local tagsKey = tags_prefix .. member - redis.log(redis.LOG_WARNING, "tagsKey: " .. tagsKey) + local tagsKey = database_prefix .. member .. ":tags" + -- redis.log(redis.LOG_NOTICE, "Rimuovo la chiave con tagsKey: " .. tagsKey) redis.call("DEL", tagsKey) end end @@ -83,26 +156,24 @@ public function handle(): void try { // Parametri dello script $shardPrefix = $prefix . 'tag:*:shard'; - $tagPrefix = $prefix . 'tags:'; + $tagPrefix = $prefix; $lockKey = $prefix . 'clean_orphans:lock'; $lockTTL = 300; // Timeout lock di 300 secondi // Esegui lo script Lua $return = $this->redis->getRedisConnection($this->option('connection_name'))->eval( $script, - 6, // Numero di parametri passati a Lua come KEYS + 5, // Numero di parametri passati a Lua come KEYS $shardPrefix, $this->numShards, $lockKey, $lockTTL, 'temp', - $tagPrefix, ); if ($return !== 'OK') { Log::error('Errore durante l\'esecuzione dello script Lua: ' . $return); } - } catch (\Exception $e) { Log::error('Errore durante l\'esecuzione dello script Lua: ' . $e->getMessage()); } diff --git a/src/Console/GetClusterNodesCommand.php b/src/Console/GetClusterNodesCommand.php new file mode 100644 index 0000000..f5328bb --- /dev/null +++ b/src/Console/GetClusterNodesCommand.php @@ -0,0 +1,39 @@ +redis = $redis; + $this->service = $service; + } + + /** + * @throws \JsonException + */ + public function handle(): void + { + try { + $array_nodi = $this->service->getClusterNodes($this->option('connection_name')); + $this->output->writeln(json_encode($array_nodi, JSON_THROW_ON_ERROR)); + } catch (\Throwable $e) { + Log::error('Errore durante il recupero dei nodi del cluster ' . $e->getMessage()); + $this->output->writeln(json_encode([], JSON_THROW_ON_ERROR)); + } + } +} diff --git a/src/Console/ListenerCommand.php b/src/Console/ListenerCommand.php index 5650d4f..b1fb1d5 100644 --- a/src/Console/ListenerCommand.php +++ b/src/Console/ListenerCommand.php @@ -3,14 +3,19 @@ namespace Padosoft\SuperCache\Console; use Illuminate\Console\Command; +use Illuminate\Support\Str; use Padosoft\SuperCache\RedisConnector; use Illuminate\Support\Facades\Log; +use Padosoft\SuperCache\SuperCacheManager; class ListenerCommand extends Command { protected $signature = 'supercache:listener {--connection_name= : (opzionale) nome della connessione redis } + {--namespace_id= : (opzionale) id del namespace da usare per suddividere i processi e da impostare se supercache.use_namespace = true } {--checkEvent= : (opzionale) se 1 si esegue controllo su attivazione evento expired di Redis } + {--host= : (opzionale) host del nodo del cluster (da impostare solo in caso di Redis in cluster) } + {--port= : (opzionale) porta del nodo del cluster (da impostare solo in caso di Redis in cluster) } '; protected $description = 'Listener per eventi di scadenza chiavi Redis'; protected RedisConnector $redis; @@ -18,8 +23,9 @@ class ListenerCommand extends Command protected int $batchSizeThreshold; // Numero di chiavi per batch protected int $timeThreshold; // Tempo massimo prima di processare il batch protected bool $useNamespace; + protected SuperCacheManager $superCache; - public function __construct(RedisConnector $redis) + public function __construct(RedisConnector $redis, SuperCacheManager $superCache) { parent::__construct(); $this->redis = $redis; @@ -27,16 +33,48 @@ public function __construct(RedisConnector $redis) $this->batchSizeThreshold = config('supercache.batch_size'); $this->timeThreshold = config('supercache.time_threshold'); // secondi $this->useNamespace = (bool) config('supercache.use_namespace', false); + $this->superCache = $superCache; } /** - * Aggiunge la chiave scaduta al batch corrente. + * Verifica se Redis è configurato per generare notifiche di scadenza. */ + protected function checkRedisNotifications(): bool + { + $checkEvent = $this->option('checkEvent'); + if ($checkEvent === null) { + return true; + } + if ((int) $checkEvent === 0) { + return true; + } + $config = $this->redis->getRedisConnection($this->option('connection_name'))->config('GET', 'notify-keyspace-events'); + + return str_contains($config['notify-keyspace-events'], 'Ex') || str_contains($config['notify-keyspace-events'], 'xE'); + } + protected function onExpireEvent(string $key): void { + $debug = 'EXPIRED $key: ' . $key . PHP_EOL . + 'Host: ' . $this->option('host') . PHP_EOL . + 'Port: ' . $this->option('port') . PHP_EOL . + 'Connection Name: ' . $this->option('connection_name') . PHP_EOL . + 'Namespace ID: ' . $this->option('namespace_id') . PHP_EOL; + // Filtro le chiavi di competenza di questo listener, ovvero quelle che iniziano con gescat_laravel_database_supercache: e che eventualemnte terminano con ns se c'è il namespace attivo // Attenzione la chiave arriva completa con il prefisso da conf redis.oprion.prefix + il prefisso della supercache // del tipo 'gescat_laravel_database_supercache:' + $prefix = config('database.redis.options')['prefix'] . config('supercache.prefix'); + $cleanedKey = str_replace(['{', '}'], '', $key); + if (!Str::startsWith($cleanedKey, $prefix)) { + return; + } + + if ($this->useNamespace && $this->option('namespace_id') !== null && !Str::endsWith($cleanedKey, 'ns' . $this->option('namespace_id'))) { + return; + } + $original_key = str_replace(config('database.redis.options')['prefix'], '', $key); + //$original_key = $this->superCache->getOriginalKey($key); $hash_key = crc32($original_key); // questo hash mi serve poi nello script LUA in quanto Redis non ha nativa la funzione crc32, ma solo il crc16 che però non è nativo in php $this->batch[] = $original_key . '|' . $hash_key; // faccio la concatenzazione con il '|' come separatore in quanto Lua non supporta array multidimensionali } @@ -62,42 +100,66 @@ protected function shouldProcessBatchByTime(): bool return false; } + protected function processBatchOnCluster(): void + { + foreach ($this->batch as $key) { + $explodeKey = explode('|', $key); + $cleanedKey = str_replace(['{', '}'], '', $explodeKey[0]); + $this->superCache->forget($cleanedKey, $this->option('connection_name'), true, true, true); + } + + $this->batch = []; + } + /** * Processa le chiavi accumulate in batch tramite uno script Lua. */ - protected function processBatch(): void + protected function processBatchOnStandalone(): void { + $debug = 'Processo batch: ' . implode(', ', $this->batch) . PHP_EOL . + 'Host: ' . $this->option('host') . PHP_EOL . + 'Port: ' . $this->option('port') . PHP_EOL; + $luaScript = <<<'LUA' local success, result = pcall(function() local keys = ARGV - local prefix = KEYS[1] - local database_prefix = string.gsub(KEYS[3], "temp", "") - local shard_count = string.gsub(KEYS[2], database_prefix, "") + local prefix = ARGV[1] + local database_prefix = ARGV[3] + local shard_count = ARGV[2] + -- redis.log(redis.LOG_NOTICE, 'prefix: ' .. prefix); + -- redis.log(redis.LOG_NOTICE, 'database_prefix: ' .. database_prefix); + -- redis.log(redis.LOG_NOTICE, 'shard_count: ' .. shard_count); for i, key in ipairs(keys) do - local row = {} - for value in string.gmatch(key, "[^|]+") do - table.insert(row, value) - end - local fullKey = database_prefix .. row[1] - -- redis.log(redis.LOG_NOTICE, 'Chiave Redis Expired: ' .. fullKey) - -- Controlla se la chiave è effettivamente scaduta - if redis.call('EXISTS', fullKey) == 0 then - local tagsKey = prefix .. 'tags:' .. row[1] - local tags = redis.call("SMEMBERS", tagsKey) - -- redis.log(redis.LOG_NOTICE, 'Tags associati: ' .. table.concat(tags, ", ")); - -- Rimuove la chiave dai set di tag associati - for j, tag in ipairs(tags) do - local shardIndex = tonumber(row[2]) % tonumber(shard_count) - local shardKey = prefix .. "tag:" .. tag .. ":shard:" .. shardIndex - redis.call("SREM", shardKey, row[1]) - -- redis.log(redis.LOG_NOTICE, 'Rimossa chiave tag: ' .. shardKey); + -- salto le prime 3 chiavi che ho usato come settings + if i > 3 then + local row = {} + for value in string.gmatch(key, "[^|]+") do + table.insert(row, value) + end + local fullKey = database_prefix .. row[1] + -- redis.log(redis.LOG_NOTICE, 'Chiave Redis Expired: ' .. fullKey) + -- Controlla se la chiave è effettivamente scaduta + if redis.call('EXISTS', fullKey) == 0 then + -- local tagsKey = prefix .. 'tags:' .. row[1] + local tagsKey = fullKey .. ':tags' + -- redis.log(redis.LOG_NOTICE, 'Tag: ' .. tagsKey); + local tags = redis.call("SMEMBERS", tagsKey) + -- redis.log(redis.LOG_NOTICE, 'Tags associati: ' .. table.concat(tags, ", ")); + -- Rimuove la chiave dai set di tag associati + for j, tag in ipairs(tags) do + local shardIndex = tonumber(row[2]) % tonumber(shard_count) + local shardKey = database_prefix .. prefix .. "tag:" .. tag .. ":shard:" .. shardIndex + -- redis.log(redis.LOG_NOTICE, 'Rimuovo la chiave dallo shard: ' .. row[1]); + redis.call("SREM", shardKey, row[1]) + -- redis.log(redis.LOG_NOTICE, 'Rimossa chiave tag: ' .. shardKey); + end + -- Rimuove l'associazione della chiave con i tag + redis.call("DEL", tagsKey) + -- redis.log(redis.LOG_NOTICE, 'Rimossa chiave tags: ' .. tagsKey); + else + redis.log(redis.LOG_WARNING, 'la chiave ' .. fullKey .. ' è ancora attiva'); end - -- Rimuove l'associazione della chiave con i tag - redis.call("DEL", tagsKey) - -- redis.log(redis.LOG_NOTICE, 'Rimossa chiave tags: ' .. tagsKey); - else - redis.log(redis.LOG_NOTICE, 'la chiave ' .. fullKey .. ' è ancora attiva'); end end end) @@ -108,66 +170,56 @@ protected function processBatch(): void return "OK" LUA; - $connection = $this->redis->getRedisConnection($this->option('connection_name')); + try { // Esegue lo script Lua passando le chiavi in batch - $return = $connection->eval( - $luaScript, - // KEYS: prefix e numero di shard - 3, - config('supercache.prefix'), - config('supercache.num_shards'), - 'temp', - // ARGV: le chiavi del batch - ...$this->batch - ); + $connection = $this->redis->getNativeRedisConnection($this->option('connection_name'), $this->option('host'), $this->option('port')); + + $return = $connection['connection']->eval($luaScript, [config('supercache.prefix'), config('supercache.num_shards'), config('database.redis.options')['prefix'], ...$this->batch], 0); if ($return !== 'OK') { Log::error('Errore durante l\'esecuzione dello script Lua: ' . $return); } // Pulisce il batch dopo il successo $this->batch = []; - } catch (\Exception $e) { + // Essendo una connessione nativa va chiusa + $connection['connection']->close(); + } catch (\Throwable $e) { Log::error('Errore durante l\'esecuzione dello script Lua: ' . $e->getMessage()); } } - /** - * Verifica se Redis è configurato per generare notifiche di scadenza. - */ - protected function checkRedisNotifications(): bool - { - $checkEvent = $this->option('checkEvent'); - if ($checkEvent === null) { - return true; - } - if ((int) $checkEvent === 0) { - return true; - } - $config = $this->redis->getRedisConnection($this->option('connection_name'))->config('GET', 'notify-keyspace-events'); - - return str_contains($config['notify-keyspace-events'], 'Ex') || str_contains($config['notify-keyspace-events'], 'xE'); - } - - public function handle() + public function handle(): void { if (!$this->checkRedisNotifications()) { $this->error('Le notifiche di scadenza di Redis non sono abilitate. Abilitale per usare il listener.'); - - return; } - $async_connection = $this->redis->getNativeRedisConnection($this->option('connection_name')); - - // Pattern per ascoltare solo gli eventi expired - $pattern = '__keyevent@' . $async_connection['database'] . '__:expired'; - // Sottoscrizione agli eventi di scadenza - $async_connection['connection']->psubscribe([$pattern], function ($redis, $channel, $message, $key) { - $this->onExpireEvent($key); - - // Verifica se è necessario processare il batch - if (count($this->batch) >= $this->batchSizeThreshold || $this->shouldProcessBatchByTime()) { - $this->processBatch(); - } - }); + try { + $async_connection = $this->redis->getNativeRedisConnection($this->option('connection_name'), $this->option('host'), $this->option('port')); + $pattern = '__keyevent@' . $async_connection['database'] . '__:expired'; + // La psubscribe è BLOCCANTE, il command resta attivo finchè non cade la connessione + $async_connection['connection']->psubscribe([$pattern], function ($redis, $channel, $message, $key) { + $this->onExpireEvent($key); + + // Verifica se è necessario processare il batch + // In caso di un cluster Redis il primo che arriva al count impostato fa scattare la pulizia. + // Possono andare in conflitto? No, perchè ogni nodo ha i suoi eventi, per cui non può esserci lo stesso evento expire su più nodi + if (count($this->batch) >= $this->batchSizeThreshold || $this->shouldProcessBatchByTime()) { + if (config('database.redis.clusters.' . ($this->option('connection_name') ?? 'default')) !== null) { + $this->processBatchOnCluster(); + } else { + $this->processBatchOnStandalone(); + } + } + }); + } catch (\Throwable $e) { + $error = 'Errore durante la sottoscrizione agli eventi EXPIRED:' . PHP_EOL . + 'Host: ' . $this->option('host') . PHP_EOL . + 'Port: ' . $this->option('port') . PHP_EOL . + 'Connection Name: ' . $this->option('connection_name') . PHP_EOL . + 'Namespace ID: ' . $this->option('namespace_id') . PHP_EOL . + 'Error: ' . $e->getMessage(); + Log::error($error); + } } } diff --git a/src/RedisConnector.php b/src/RedisConnector.php index 99a6449..41d13e9 100644 --- a/src/RedisConnector.php +++ b/src/RedisConnector.php @@ -6,60 +6,52 @@ class RedisConnector { - protected $connection; - - public function __construct() + public function getRedisConnection(?string $connection_name = null): \Illuminate\Redis\Connections\Connection { - $this->connection = config('supercache.connection'); - } - - public function getRedisConnection(?string $connection_name = null) - { - return Redis::connection($connection_name ?? $this->connection); + return Redis::connection($connection_name ?? config('supercache.connection')); } /** - * Establishes and returns a native Redis connection. - * Questo metodo ritorna una cionnessione redis senza utilizzare il wrapper di Laravel. - * La connessione nativa è necessaria per la sottoscrizione agli eventi (Es. psubscribe([...)) in quanto Laravel gestisce solo connessioni sync, - * mentre per le sottoscrizioni è necessaria una connessione async + * Establishes a native Redis connection based on the provided connection name and optional host and port. * - * @param string|null $connection_name Optional. The name of the Redis connection to establish. If not provided, the default connection is used. - * @return array The Redis connection instance and database. + * @param string|null $connection_name The name of the Redis connection configuration to use. Defaults to 'default'. + * @param string|null $host The hostname to use for the connection. If not provided, it will be retrieved from the configuration. + * @param string|null $port The port number to use for the connection. If not provided, it will be retrieved from the configuration. + * @return array|null Returns an associative array with the Redis connection instance and the selected database, or null on failure. + * The array contains: + * - 'connection': The instance of the native Redis connection. + * - 'database': The selected database index. */ - public function getNativeRedisConnection(?string $connection_name = null): array + public function getNativeRedisConnection(?string $connection_name = null, ?string $host = null, ?string $port = null): ?array { - $isCluster = config('database.redis.clusters.' . ($connection_name ?? 'default')) !== null ? true : false; - if ($isCluster) { - $config = config('database.redis.clusters.' . ($connection_name ?? 'default')); - $url = $config[0]['host'] . ':' . $config[0]['port']; - $nativeRedisCluster = new \RedisCluster( - null, // Nome del cluster (può essere null) - [$url], // Nodo master - -1, // Timeout connessione - -1, // Timeout lettura - true, // Persistente - ($config[0]['password'] !== null && $config[0]['password'] !== '' ? $config[0]['password'] : null) // Password se necessaria - ); - - // Nel cluster c'è sempre un unico databse - return ['connection' => $nativeRedisCluster, 'database' => 0]; - } // Crea una nuova connessione nativa Redis + $config = config('database.redis.clusters.' . ($connection_name ?? config('supercache.connection'))); + if ($config !== null && ($host === null || $port === null)) { + // Sono nel caso del cluster, host e port sono obbligatori in quanto le connessioni vanno stabilite per ogni nodo del cluster + throw new \RuntimeException('Host e port non possono essere null per le connessioni in cluster'); + } + if ($config === null) { + // Sono nel caso di una connessione standalone + $config = config('database.redis.' . ($connection_name ?? config('supercache.connection'))); + } $nativeRedis = new \Redis(); - // Connessione al server Redis (no cluster) - $config = config('database.redis.' . ($connection_name ?? 'default')); - $nativeRedis->connect($config['host'], $config['port']); + if ($host !== null && $port !== null) { + // Se ho host e port (caso del cluster) uso questi + $nativeRedis->connect($host, $port); + } else { + // Altrimenti utilizzo host e port dalla configurazione della connessione standalone + $nativeRedis->connect($config['host'], $config['port']); + } // Autenticazione con username e password (se configurati) - if ($config['username'] !== null && $config['password'] !== null && $config['password'] !== '' && $config['username'] !== '') { + if (array_key_exists('username', $config) && $config['username'] !== '' && array_key_exists('password', $config) && $config['password'] !== '') { $nativeRedis->auth([$config['username'], $config['password']]); - } elseif ($config['password'] !== null && $config['password'] !== '') { + } elseif (array_key_exists('password', $config) && $config['password'] !== '') { $nativeRedis->auth($config['password']); // Per versioni Redis senza ACL } - // Seleziono il database corretto - $database = ($config['database'] !== null && $config['database'] !== '') ? (int) $config['database'] : 0; + // Seleziono il database corretto (Per il cluster è sempre 0) + $database = (array_key_exists('database', $config) && $config['database'] !== '') ? (int) $config['database'] : 0; $nativeRedis->select($database); return ['connection' => $nativeRedis, 'database' => $database]; @@ -68,6 +60,6 @@ public function getNativeRedisConnection(?string $connection_name = null): array // Metodo per ottimizzare le operazioni Redis con pipeline public function pipeline($callback, ?string $connection_name = null) { - return $this->getRedisConnection($connection_name)->pipeline($callback); + return $this->getRedisConnection(($connection_name ?? config('supercache.connection')))->pipeline($callback); } } diff --git a/src/Service/GetClusterNodesService.php b/src/Service/GetClusterNodesService.php new file mode 100644 index 0000000..ac7f42a --- /dev/null +++ b/src/Service/GetClusterNodesService.php @@ -0,0 +1,71 @@ +redis = $redis; + } + + /** + * Recupera l'elenco dei nodi in un cluster Redis. + * + * Questo metodo si connette al cluster Redis ed estrae l'elenco dei nodi, concentrandosi principalmente + * sui nodi master. Include anche una logica opzionale per raccogliere tutti i nodi, inclusi gli slave, se necessario. + * L'elenco risultante contiene informazioni uniche host:port per ciascun nodo. + * + * @param string|null $connection Stringa opzionale per identificare la connessione. Se null, verrà utilizzata la connessione predefinita. + * @return array Un array di stringhe che rappresenta i host:port di ciascun nodo all'interno del cluster. + */ + public function getClusterNodes(?string $connection = null): array + { + try { + $array_nodi = []; // Alla fine in questo array dovrei avere le informazioni host:port di tutti i nodi che compongono il cluster + $redisConnection = $this->redis->getRedisConnection($connection); + // 1) Recupero i nodi master dalla connessione, in una configurazione standard in genere ci sono 3 master e "n" slave + $masters = $redisConnection->_masters(); + // 2) Per ogni nodo master mi faccio dare i nodi a lui collegati + foreach ($masters as $master) { + $array_nodi[] = $master[0] . ':' . $master[1]; + + // Dovrebbe essere sufficente avere i nodi master in quanto i nodi slave sono solo delle repliche e non inviano eventi, + // inoltre le loro chiavi sono repliche di chiavi presenti in shard nei master + // Se poi ci fossero dei problemi o si vede che perdiamo rroba, il copdoce sotto serve per avere tutti i nodi del cluster. + // Attenzione che il comando 'CLUSTER NODES' su AWS non funziona, ma va abilitato + + /* + $nodeInfo = $redisConnection->rawCommand($master[0] . ':' . $master[1], 'CLUSTER', 'NODES'); + // Questo comando restituisce una string che rappresenta in formato CSV le info sui nodi + // Ogni riga è un nodo, ogni riga ha poi "n" colonne, a noi interessa la seconda colonna con host:port@port + $splittedRow = explode(PHP_EOL, $nodeInfo); + + foreach ($splittedRow as $row) { + $splittedColumn = explode(' ', $row); + if (array_key_exists(1, $splittedColumn)) { + $infoNodo = $splittedColumn[1]; + $splittedInfoNodo = explode('@', $infoNodo); + $hostAndPort = $splittedInfoNodo[0]; + $array_nodi[] = $hostAndPort; + } + } + */ + } + + // 3) Tolgo i doppioni, infatti per la ridondanza ogni master ha in comune con glia ltri alcuni nodi + //$array_nodi = array_unique($array_nodi); + // Stampa l'array come JSON per il parsing lato script bash + return $array_nodi; + } catch (\Throwable $e) { + Log::error('Errore durante il recupero dei nodi del cluster ' . $e->getMessage()); + + return []; + } + } +} diff --git a/src/SuperCacheManager.php b/src/SuperCacheManager.php index bb930d0..ee87346 100644 --- a/src/SuperCacheManager.php +++ b/src/SuperCacheManager.php @@ -12,6 +12,12 @@ class SuperCacheManager protected int $numShards; public string $prefix; public bool $useNamespace; + + /** + * Questa proprietà viene settata dinamicamente dove serve in base al nome della connessione + * + * @deprecated + */ public bool $isCluster = false; public function __construct(RedisConnector $redis) @@ -53,19 +59,27 @@ public function put(string $key, mixed $value, ?int $ttl = null, ?string $connec { // Calcola la chiave con o senza namespace in base alla configurazione $finalKey = $this->getFinalKey($key); - $this->redis->getRedisConnection($connection_name)->set($finalKey, $this->serializeForRedis($value)); - + /** + * $this->redis->getRedisConnection($connection_name)->set($finalKey, $this->serializeForRedis($value)); + * + * if ($ttl !== null) { + * $this->redis->getRedisConnection($connection_name)->expire($finalKey, $ttl); + * } + */ if ($ttl !== null) { - $this->redis->getRedisConnection($connection_name)->expire($finalKey, $ttl); + $this->redis->getRedisConnection($connection_name)->setEx($finalKey, $ttl, $this->serializeForRedis($value)); + + return; } + $this->redis->getRedisConnection($connection_name)->set($finalKey, $this->serializeForRedis($value)); } - public function getTTLKey(string $key, ?string $connection_name = null): int + public function getTTLKey(string $key, ?string $connection_name = null, bool $isWithTags = false): int { // Calcola la chiave con o senza namespace in base alla configurazione - $finalKey = $this->getFinalKey($key); + $finalKey = $this->getFinalKey($key, $isWithTags); - return $this->redis->getRedisConnection($connection_name)->ttl($finalKey); + return $this->redis->getRedisConnection($connection_name)->ttl($isWithTags ? ('{' . $finalKey . '}') : $finalKey); } /** @@ -74,35 +88,39 @@ public function getTTLKey(string $key, ?string $connection_name = null): int */ public function putWithTags(string $key, mixed $value, array $tags, ?int $ttl = null, ?string $connection_name = null): void { - $finalKey = $this->getFinalKey($key); + $finalKey = $this->getFinalKey($key, true); + $finalTags = $this->getFinalTag($finalKey, true); // Usa pipeline solo se non è un cluster - if (!$this->isCluster) { - $this->redis->pipeline(function ($pipe) use ($finalKey, $value, $tags, $ttl) { - $pipe->set($finalKey, $this->serializeForRedis($value)); - + $isCluster = config('database.redis.clusters.' . ($connection_name ?? 'default')) !== null; + if (!$isCluster) { + $this->redis->pipeline(function ($pipe) use ($finalKey, $finalTags, $value, $tags, $ttl) { + // Qui devo mettere le {} perchè così mi assicuro che la chiave e i suoi tags stiano nello stesso has if ($ttl !== null) { - $pipe->expire($finalKey, $ttl); + $pipe->setEx('{' . $finalKey . '}', $ttl, $this->serializeForRedis($value)); + } else { + $pipe->set('{' . $finalKey . '}', $this->serializeForRedis($value)); } foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, $finalKey); - $pipe->sadd($shard, $finalKey); + $shard = $this->getShardNameForTag($tag, '{' . $finalKey . '}'); + $pipe->sadd($shard, '{' . $finalKey . '}'); } - $pipe->sadd($this->prefix . 'tags:' . $finalKey, ...$tags); + $pipe->sadd($finalTags, ...$tags); }, $connection_name); } else { - $this->redis->getRedisConnection($connection_name)->set($finalKey, $this->serializeForRedis($value)); if ($ttl !== null) { - $this->redis->getRedisConnection($connection_name)->expire($finalKey, $ttl); + $this->redis->getRedisConnection($connection_name)->setEx('{' . $finalKey . '}', $ttl, $this->serializeForRedis($value)); + } else { + $result = $this->redis->getRedisConnection($connection_name)->set('{' . $finalKey . '}', $this->serializeForRedis($value)); } foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, $finalKey); - $this->redis->getRedisConnection($connection_name)->sadd($shard, $finalKey); + $shard = $this->getShardNameForTag($tag, '{' . $finalKey . '}'); + $result = $this->redis->getRedisConnection($connection_name)->sadd($shard, '{' . $finalKey . '}'); } - $this->redis->getRedisConnection($connection_name)->sadd($this->prefix . 'tags:' . $finalKey, ...$tags); + $this->redis->getRedisConnection($connection_name)->sadd($finalTags, ...$tags); } } @@ -122,7 +140,7 @@ public function putWithTags(string $key, mixed $value, array $tags, ?int $ttl = */ public function rememberWithTags($key, array $tags, \Closure $callback, ?int $ttl = null, ?string $connection_name = null) { - $finalKey = $this->getFinalKey($key); + $finalKey = $this->getFinalKey($key, true); $value = $this->get($finalKey, $connection_name); // Se esiste già, ok la ritorno @@ -141,9 +159,13 @@ public function rememberWithTags($key, array $tags, \Closure $callback, ?int $tt * Recupera un valore dalla cache. * Il valore della chiave sarà deserializzato tranne nel caso di valori numerici */ - public function get(string $key, ?string $connection_name = null): mixed + public function get(string $key, ?string $connection_name = null, bool $isWithTags = false): mixed { - $finalKey = $this->getFinalKey($key); + $finalKey = $this->getFinalKey($key, $isWithTags); + if ($isWithTags) { + $finalKey = '{' . $finalKey . '}'; + } + $value = $this->redis->getRedisConnection($connection_name)->get($finalKey); return $value ? $this->unserializeForRedis($value) : null; @@ -152,35 +174,39 @@ public function get(string $key, ?string $connection_name = null): mixed /** * Rimuove una chiave dalla cache e dai suoi set di tag. */ - public function forget(string $key, ?string $connection_name = null, ?bool $isFinalKey = false): void + public function forget(string $key, ?string $connection_name = null, bool $isFinalKey = false, bool $isWithTags = false, bool $onlyTags = false): void { if ($isFinalKey) { $finalKey = $key; } else { - $finalKey = $this->getFinalKey($key); + $finalKey = $this->getFinalKey($key, $isWithTags); } - + $finalTags = $this->getFinalTag($finalKey, $isWithTags); // Recupera i tag associati alla chiave - $tags = $this->redis->getRedisConnection($connection_name)->smembers($this->prefix . 'tags:' . $finalKey); - - if (!$this->isCluster) { - $this->redis->pipeline(function ($pipe) use ($tags, $finalKey) { + $tags = $this->redis->getRedisConnection($connection_name)->smembers($finalTags); + $isCluster = config('database.redis.clusters.' . ($connection_name ?? 'default')) !== null; + if (!$isCluster) { + $this->redis->pipeline(function ($pipe) use ($isWithTags, $onlyTags, $tags, $finalKey, $finalTags) { foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, $finalKey); - $pipe->srem($shard, $finalKey); + $shard = $this->getShardNameForTag($tag, ($isWithTags ? ('{' . $finalKey . '}') : $finalKey)); + $pipe->srem($shard, ($isWithTags ? ('{' . $finalKey . '}') : $finalKey)); } - $pipe->del($this->prefix . 'tags:' . $finalKey); - $pipe->del($finalKey); + $pipe->del($finalTags); + if (!$onlyTags) { + $pipe->del($isWithTags ? ('{' . $finalKey . '}') : $finalKey); + } }, $connection_name); } else { foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, $finalKey); - $this->redis->getRedisConnection($connection_name)->srem($shard, $finalKey); + $shard = $this->getShardNameForTag($tag, ($isWithTags ? ('{' . $finalKey . '}') : $finalKey)); + $this->redis->getRedisConnection($connection_name)->srem($shard, ($isWithTags ? ('{' . $finalKey . '}') : $finalKey)); } - $this->redis->getRedisConnection($connection_name)->del($this->prefix . 'tags:' . $finalKey); - $this->redis->getRedisConnection($connection_name)->del($finalKey); + $this->redis->getRedisConnection($connection_name)->del($finalTags); + if (!$onlyTags) { + $this->redis->getRedisConnection($connection_name)->del($isWithTags ? ('{' . $finalKey . '}') : $finalKey); + } } } @@ -192,7 +218,7 @@ public function flushByTags(array $tags, ?string $connection_name = null): void $keys = $this->getKeysOfTag($tag, $connection_name); foreach ($keys as $key) { // Con questo cancello sia i tag che le chiavi - $this->forget($key, $connection_name, true); + $this->forget($key, $connection_name, true, true); } } } @@ -202,16 +228,20 @@ public function flushByTags(array $tags, ?string $connection_name = null): void */ public function getTagsOfKey(string $key, ?string $connection_name = null): array { - $finalKey = $this->getFinalKey($key); + $finalKey = $this->getFinalKey($key, true); + $finalTags = $this->getFinalTag($finalKey, true); - return $this->redis->getRedisConnection($connection_name)->smembers($this->prefix . 'tags:' . $finalKey); + return $this->redis->getRedisConnection($connection_name)->smembers($finalTags); } /** * Recupera tutte le chiavi associate a un tag. */ - public function getKeysOfTag(string $tag, ?string $connection_name = null): array + public function getKeysOfTag(string $tag, ?string $connection_name = null, bool $isfinalTag = false): array { + if ($isfinalTag) { + return $this->redis->getRedisConnection($connection_name)->smembers($tag); + } $keys = []; // Itera attraverso tutti gli shard del tag @@ -240,17 +270,26 @@ public function getShardNameForTag(string $tag, string $key): string * * Se l'opzione 'use_namespace' è disattivata, la chiave sarà formata senza namespace. */ - public function getFinalKey(string $key): string + public function getFinalKey(string $key, bool $isWithTags = false): string { // Se il namespace è abilitato, calcola la chiave con namespace come suffisso if ($this->useNamespace) { $namespace = $this->calculateNamespace($key); - return $this->prefix . $key . ':' . $namespace; + return $this->prefix . $key . ':' . ($isWithTags ? 'byTags:' : '') . $namespace; } // Se il namespace è disabilitato, usa la chiave senza suffisso - return $this->prefix . $key; + return $this->prefix . $key . ($isWithTags ? ':byTags' : ''); + } + + public function getFinalTag(string $finalKey, bool $isWithTags = false): string + { + if ($isWithTags) { + return '{' . $finalKey . '}:tags'; + } + + return $finalKey . ':tags'; } /** @@ -264,11 +303,15 @@ public function flush(?string $connection_name = null): void /** * Check if a cache key exists without retrieving the value. */ - public function has(string $key, ?string $connection_name = null): bool + public function has(string $key, ?string $connection_name = null, bool $isWithTags = false, bool $isfinalKey = false): bool { - $finalKey = $this->getFinalKey($key); + if ($isfinalKey) { + $finalKey = $key; + } else { + $finalKey = $this->getFinalKey($key, $isWithTags); + } - return $this->redis->getRedisConnection($connection_name)->exists($finalKey) > 0; + return $this->redis->getRedisConnection($connection_name)->exists(($isWithTags && !$isfinalKey) ? ('{' . $finalKey . '}') : $finalKey) > 0; } /** @@ -319,11 +362,13 @@ public function getKeys(array $patterns, ?string $connection_name = null): array ] )) { $iterator = $keys[0]; + foreach ($keys[1] as $key) { - $tempArrKeys[] = $key; if ($key === null) { continue; } + $tempArrKeys[] = $key; + $original_key = $this->getOriginalKey($key); $value = $this->get($original_key); $results[$original_key] = $value; @@ -336,7 +381,7 @@ public function getKeys(array $patterns, ?string $connection_name = null): array public function getOriginalKey(string $finalKey): string { - $originalKey = str_replace([config('database.redis.options')['prefix'], $this->prefix], '', $finalKey); + $originalKey = str_replace([config('database.redis.options')['prefix'], $this->prefix, '{', '}'], '', $finalKey); if (!$this->useNamespace) { return $originalKey; } @@ -353,7 +398,7 @@ public function getOriginalKey(string $finalKey): string */ public function lock(string $key, ?string $connection_name = null, int $ttl = 10, string $value = '1'): bool { - $finalKey = $this->getFinalKey($key); + $finalKey = $this->getFinalKey($key) . ':semaphore'; $luaScript = <<<'LUA' if redis.call("SET", KEYS[1], ARGV[2], "NX", "EX", tonumber(ARGV[1])) then return 1 @@ -369,6 +414,7 @@ public function lock(string $key, ?string $connection_name = null, int $ttl = 10 $ttl, $value ); + return $result === 1; } @@ -380,7 +426,7 @@ public function lock(string $key, ?string $connection_name = null, int $ttl = 10 */ public function unLock(string $key, ?string $connection_name = null): void { - $finalKey = $this->getFinalKey($key); + $finalKey = $this->getFinalKey($key) . ':semaphore'; $luaScript = <<<'LUA' redis.call('DEL', KEYS[1]); LUA; diff --git a/src/SuperCacheServiceProvider.php b/src/SuperCacheServiceProvider.php index c4a57e0..a57c590 100644 --- a/src/SuperCacheServiceProvider.php +++ b/src/SuperCacheServiceProvider.php @@ -4,6 +4,7 @@ use Illuminate\Support\ServiceProvider; use Padosoft\SuperCache\Console\GetAllTagsOfKeyCommand; +use Padosoft\SuperCache\Console\GetClusterNodesCommand; use Padosoft\SuperCache\Console\ListenerCommand; use Padosoft\SuperCache\Console\CleanOrphanedKeysCommand; class SuperCacheServiceProvider extends ServiceProvider @@ -36,6 +37,7 @@ public function boot() GetAllTagsOfKeyCommand::class, ListenerCommand::class, CleanOrphanedKeysCommand::class, + GetClusterNodesCommand::class, ]); } } diff --git a/tests/Unit/SuperCacheManagerTest.php b/tests/Unit/SuperCacheManagerTest.php index 3a4c220..1ced9de 100644 --- a/tests/Unit/SuperCacheManagerTest.php +++ b/tests/Unit/SuperCacheManagerTest.php @@ -94,10 +94,10 @@ public function test_put_with_tags(string $key, mixed $value, array $tags, ?int $this->superCache->useNamespace = $namespaceEnabled; $this->superCache->putWithTags($key, $value, $tags, $ttl); // La chiave deve essere stata creata - $this->assertEquals($value, $this->superCache->get($key)); + $this->assertEquals($value, $this->superCache->get($key, null, true)); // La chiave deve avere i tag corretti $this->assertEquals($tags, $this->superCache->getTagsOfKey($key)); - $ttlSet = $this->superCache->getTTLKey($key); + $ttlSet = $this->superCache->getTTLKey($key, null, true); if ($ttl !== null) { // Verifica che il TTL sia un valore positivo $this->assertGreaterThan(0, $ttlSet); @@ -119,13 +119,13 @@ public function test_forget(string $key, array $tags, bool $namespaceEnabled): v $this->superCache->putWithTags($key, '1', $tags); } - // MI assicuro che la chiave sia stata inserita prima di rimuoverla e controllo anche i tag - $this->assertEquals('1', $this->superCache->get($key)); + // MI assicuro che la chiave sia stata inserita prima di rimuoverla e controllo anche i tag + $this->assertEquals('1', $this->superCache->get($key, null,count($tags) > 0)); $tagsCached = $this->superCache->getTagsOfKey($key); $this->assertEquals($tags, $tagsCached); - $this->superCache->forget($key); + $this->superCache->forget($key, null, false, count($tags) > 0); // Dopo la rimozione devono essere spariti chiave e tag - $this->assertNull($this->superCache->get($key)); + $this->assertNull($this->superCache->get($key,null,count($tags) > 0)); foreach ($tagsCached as $tag) { $shard = $this->superCache->getShardNameForTag($tag, $key); $this->assertNull($this->superCache->get($shard)); @@ -143,8 +143,12 @@ public function test_flush(): void public function test_has(): void { $this->superCache->put('key1', 'value1'); + $this->superCache->putWithTags('key2', 'value1', ['tag1']); $this->assertTrue($this->superCache->has('key1')); + $this->assertTrue($this->superCache->has('key2', null, true)); + $finalKey = $this->superCache->getFinalKey('key2', true); + $this->assertTrue($this->superCache->has('{' . $finalKey . '}', null, true, true)); $this->assertFalse($this->superCache->has('non_existing_key')); } From 41190cb293c93fcd96e4b0f8800101a10d7b1054 Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Fri, 20 Dec 2024 11:02:51 +0100 Subject: [PATCH 02/13] nuova versione per il cluster --- src/SuperCacheManager.php | 41 ++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/SuperCacheManager.php b/src/SuperCacheManager.php index ee87346..e13fb5b 100644 --- a/src/SuperCacheManager.php +++ b/src/SuperCacheManager.php @@ -67,11 +67,11 @@ public function put(string $key, mixed $value, ?int $ttl = null, ?string $connec * } */ if ($ttl !== null) { - $this->redis->getRedisConnection($connection_name)->setEx($finalKey, $ttl, $this->serializeForRedis($value)); + $this->redis->getRedisConnection($connection_name)->setEx('{' . $finalKey . '}', $ttl, $this->serializeForRedis($value)); return; } - $this->redis->getRedisConnection($connection_name)->set($finalKey, $this->serializeForRedis($value)); + $this->redis->getRedisConnection($connection_name)->set('{' . $finalKey . '}', $this->serializeForRedis($value)); } public function getTTLKey(string $key, ?string $connection_name = null, bool $isWithTags = false): int @@ -79,7 +79,7 @@ public function getTTLKey(string $key, ?string $connection_name = null, bool $is // Calcola la chiave con o senza namespace in base alla configurazione $finalKey = $this->getFinalKey($key, $isWithTags); - return $this->redis->getRedisConnection($connection_name)->ttl($isWithTags ? ('{' . $finalKey . '}') : $finalKey); + return $this->redis->getRedisConnection($connection_name)->ttl('{' . $finalKey . '}'); } /** @@ -89,7 +89,7 @@ public function getTTLKey(string $key, ?string $connection_name = null, bool $is public function putWithTags(string $key, mixed $value, array $tags, ?int $ttl = null, ?string $connection_name = null): void { $finalKey = $this->getFinalKey($key, true); - $finalTags = $this->getFinalTag($finalKey, true); + $finalTags = $this->getFinalTag($finalKey); // Usa pipeline solo se non è un cluster $isCluster = config('database.redis.clusters.' . ($connection_name ?? 'default')) !== null; if (!$isCluster) { @@ -162,11 +162,8 @@ public function rememberWithTags($key, array $tags, \Closure $callback, ?int $tt public function get(string $key, ?string $connection_name = null, bool $isWithTags = false): mixed { $finalKey = $this->getFinalKey($key, $isWithTags); - if ($isWithTags) { - $finalKey = '{' . $finalKey . '}'; - } - $value = $this->redis->getRedisConnection($connection_name)->get($finalKey); + $value = $this->redis->getRedisConnection($connection_name)->get('{' . $finalKey . '}'); return $value ? $this->unserializeForRedis($value) : null; } @@ -181,31 +178,31 @@ public function forget(string $key, ?string $connection_name = null, bool $isFin } else { $finalKey = $this->getFinalKey($key, $isWithTags); } - $finalTags = $this->getFinalTag($finalKey, $isWithTags); + $finalTags = $this->getFinalTag($finalKey); // Recupera i tag associati alla chiave $tags = $this->redis->getRedisConnection($connection_name)->smembers($finalTags); $isCluster = config('database.redis.clusters.' . ($connection_name ?? 'default')) !== null; if (!$isCluster) { $this->redis->pipeline(function ($pipe) use ($isWithTags, $onlyTags, $tags, $finalKey, $finalTags) { foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, ($isWithTags ? ('{' . $finalKey . '}') : $finalKey)); - $pipe->srem($shard, ($isWithTags ? ('{' . $finalKey . '}') : $finalKey)); + $shard = $this->getShardNameForTag($tag, '{' . $finalKey . '}'); + $pipe->srem($shard, '{' . $finalKey . '}'); } $pipe->del($finalTags); if (!$onlyTags) { - $pipe->del($isWithTags ? ('{' . $finalKey . '}') : $finalKey); + $pipe->del('{' . $finalKey . '}'); } }, $connection_name); } else { foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, ($isWithTags ? ('{' . $finalKey . '}') : $finalKey)); - $this->redis->getRedisConnection($connection_name)->srem($shard, ($isWithTags ? ('{' . $finalKey . '}') : $finalKey)); + $shard = $this->getShardNameForTag($tag, '{' . $finalKey . '}'); + $this->redis->getRedisConnection($connection_name)->srem($shard, '{' . $finalKey . '}'); } $this->redis->getRedisConnection($connection_name)->del($finalTags); if (!$onlyTags) { - $this->redis->getRedisConnection($connection_name)->del($isWithTags ? ('{' . $finalKey . '}') : $finalKey); + $this->redis->getRedisConnection($connection_name)->del('{' . $finalKey . '}'); } } } @@ -229,7 +226,7 @@ public function flushByTags(array $tags, ?string $connection_name = null): void public function getTagsOfKey(string $key, ?string $connection_name = null): array { $finalKey = $this->getFinalKey($key, true); - $finalTags = $this->getFinalTag($finalKey, true); + $finalTags = $this->getFinalTag($finalKey); return $this->redis->getRedisConnection($connection_name)->smembers($finalTags); } @@ -283,13 +280,9 @@ public function getFinalKey(string $key, bool $isWithTags = false): string return $this->prefix . $key . ($isWithTags ? ':byTags' : ''); } - public function getFinalTag(string $finalKey, bool $isWithTags = false): string + public function getFinalTag(string $finalKey): string { - if ($isWithTags) { - return '{' . $finalKey . '}:tags'; - } - - return $finalKey . ':tags'; + return '{' . $finalKey . '}:tags'; } /** @@ -308,10 +301,10 @@ public function has(string $key, ?string $connection_name = null, bool $isWithTa if ($isfinalKey) { $finalKey = $key; } else { - $finalKey = $this->getFinalKey($key, $isWithTags); + $finalKey = '{' . $this->getFinalKey($key, $isWithTags) . '}'; } - return $this->redis->getRedisConnection($connection_name)->exists(($isWithTags && !$isfinalKey) ? ('{' . $finalKey . '}') : $finalKey) > 0; + return $this->redis->getRedisConnection($connection_name)->exists($finalKey) > 0; } /** From 62439ebce11551a11b70dd0cfd5f2c9bfbea32cc Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Fri, 20 Dec 2024 12:55:59 +0100 Subject: [PATCH 03/13] nuova versione per il cluster --- src/SuperCacheManager.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/SuperCacheManager.php b/src/SuperCacheManager.php index e13fb5b..e04b9a7 100644 --- a/src/SuperCacheManager.php +++ b/src/SuperCacheManager.php @@ -59,13 +59,6 @@ public function put(string $key, mixed $value, ?int $ttl = null, ?string $connec { // Calcola la chiave con o senza namespace in base alla configurazione $finalKey = $this->getFinalKey($key); - /** - * $this->redis->getRedisConnection($connection_name)->set($finalKey, $this->serializeForRedis($value)); - * - * if ($ttl !== null) { - * $this->redis->getRedisConnection($connection_name)->expire($finalKey, $ttl); - * } - */ if ($ttl !== null) { $this->redis->getRedisConnection($connection_name)->setEx('{' . $finalKey . '}', $ttl, $this->serializeForRedis($value)); From e49fd30c7621142badd7ac6cdf94b7ba1bfa9f4a Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Fri, 20 Dec 2024 14:16:13 +0100 Subject: [PATCH 04/13] fix remember --- src/SuperCacheManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SuperCacheManager.php b/src/SuperCacheManager.php index e04b9a7..660c116 100644 --- a/src/SuperCacheManager.php +++ b/src/SuperCacheManager.php @@ -143,7 +143,7 @@ public function rememberWithTags($key, array $tags, \Closure $callback, ?int $tt $value = $callback(); - $this->putWithTags($finalKey, $value, $tags, $ttl, $connection_name); + $this->putWithTags($key, $value, $tags, $ttl, $connection_name); return $value; } From b8ce6b38fdd96b38ea16e3b2c46820a8b4c0c58a Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Fri, 20 Dec 2024 15:34:39 +0100 Subject: [PATCH 05/13] fix forget --- src/SuperCacheManager.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SuperCacheManager.php b/src/SuperCacheManager.php index 660c116..4d3466f 100644 --- a/src/SuperCacheManager.php +++ b/src/SuperCacheManager.php @@ -167,7 +167,7 @@ public function get(string $key, ?string $connection_name = null, bool $isWithTa public function forget(string $key, ?string $connection_name = null, bool $isFinalKey = false, bool $isWithTags = false, bool $onlyTags = false): void { if ($isFinalKey) { - $finalKey = $key; + $finalKey = str_replace(['{', '}'], '', $key); } else { $finalKey = $this->getFinalKey($key, $isWithTags); } @@ -292,12 +292,12 @@ public function flush(?string $connection_name = null): void public function has(string $key, ?string $connection_name = null, bool $isWithTags = false, bool $isfinalKey = false): bool { if ($isfinalKey) { - $finalKey = $key; + $finalKey = str_replace(['{', '}'], '', $key); } else { - $finalKey = '{' . $this->getFinalKey($key, $isWithTags) . '}'; + $finalKey = $this->getFinalKey($key, $isWithTags); } - return $this->redis->getRedisConnection($connection_name)->exists($finalKey) > 0; + return $this->redis->getRedisConnection($connection_name)->exists('{' . $finalKey . '}') > 0; } /** From a18616514820c0f8c57d64d313f0f4a6204078c5 Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Tue, 31 Dec 2024 11:40:56 +0100 Subject: [PATCH 06/13] fix seeweb timeout --- src/RedisConnector.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/RedisConnector.php b/src/RedisConnector.php index 41d13e9..5530214 100644 --- a/src/RedisConnector.php +++ b/src/RedisConnector.php @@ -37,10 +37,10 @@ public function getNativeRedisConnection(?string $connection_name = null, ?strin $nativeRedis = new \Redis(); if ($host !== null && $port !== null) { // Se ho host e port (caso del cluster) uso questi - $nativeRedis->connect($host, $port); + $nativeRedis->connect($host, $port, 0, null, 0, -1); } else { // Altrimenti utilizzo host e port dalla configurazione della connessione standalone - $nativeRedis->connect($config['host'], $config['port']); + $nativeRedis->connect($config['host'], $config['port'], 0, null, 0, -1); } // Autenticazione con username e password (se configurati) @@ -53,6 +53,7 @@ public function getNativeRedisConnection(?string $connection_name = null, ?strin // Seleziono il database corretto (Per il cluster è sempre 0) $database = (array_key_exists('database', $config) && $config['database'] !== '') ? (int) $config['database'] : 0; $nativeRedis->select($database); + $nativeRedis->setOption(\Redis::OPT_READ_TIMEOUT, -1); return ['connection' => $nativeRedis, 'database' => $database]; } From c51eb79752e960e3beb20abd448a40929cdb464a Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Thu, 2 Jan 2025 09:59:53 +0100 Subject: [PATCH 07/13] new fix seeweb timeout --- src/RedisConnector.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/RedisConnector.php b/src/RedisConnector.php index 5530214..6575c79 100644 --- a/src/RedisConnector.php +++ b/src/RedisConnector.php @@ -37,10 +37,10 @@ public function getNativeRedisConnection(?string $connection_name = null, ?strin $nativeRedis = new \Redis(); if ($host !== null && $port !== null) { // Se ho host e port (caso del cluster) uso questi - $nativeRedis->connect($host, $port, 0, null, 0, -1); + $nativeRedis->connect($host, $port); } else { // Altrimenti utilizzo host e port dalla configurazione della connessione standalone - $nativeRedis->connect($config['host'], $config['port'], 0, null, 0, -1); + $nativeRedis->connect($config['host'], $config['port']); } // Autenticazione con username e password (se configurati) @@ -53,7 +53,7 @@ public function getNativeRedisConnection(?string $connection_name = null, ?strin // Seleziono il database corretto (Per il cluster è sempre 0) $database = (array_key_exists('database', $config) && $config['database'] !== '') ? (int) $config['database'] : 0; $nativeRedis->select($database); - $nativeRedis->setOption(\Redis::OPT_READ_TIMEOUT, -1); + //$nativeRedis->setOption(\Redis::OPT_READ_TIMEOUT, -1); return ['connection' => $nativeRedis, 'database' => $database]; } From 07b7ac80436422b5d447a3591966c1b6ef65f234 Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Thu, 2 Jan 2025 17:18:05 +0100 Subject: [PATCH 08/13] new fix seeweb timeoutcon redis 6 --- src/RedisConnector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/RedisConnector.php b/src/RedisConnector.php index 6575c79..7db8811 100644 --- a/src/RedisConnector.php +++ b/src/RedisConnector.php @@ -38,9 +38,10 @@ public function getNativeRedisConnection(?string $connection_name = null, ?strin if ($host !== null && $port !== null) { // Se ho host e port (caso del cluster) uso questi $nativeRedis->connect($host, $port); + $nativeRedis->connect($host, $port, 0, null, 0, -1); } else { // Altrimenti utilizzo host e port dalla configurazione della connessione standalone - $nativeRedis->connect($config['host'], $config['port']); + $nativeRedis->connect($config['host'], $config['port'], 0, null, 0, -1); } // Autenticazione con username e password (se configurati) From 58187af1773a90b81833f5bcb4ecd4d27fe806bf Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Thu, 2 Jan 2025 17:20:52 +0100 Subject: [PATCH 09/13] new fix seeweb timeoutcon redis 6 --- src/RedisConnector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RedisConnector.php b/src/RedisConnector.php index 7db8811..1f36212 100644 --- a/src/RedisConnector.php +++ b/src/RedisConnector.php @@ -37,7 +37,7 @@ public function getNativeRedisConnection(?string $connection_name = null, ?strin $nativeRedis = new \Redis(); if ($host !== null && $port !== null) { // Se ho host e port (caso del cluster) uso questi - $nativeRedis->connect($host, $port); + //$nativeRedis->connect($host, $port); $nativeRedis->connect($host, $port, 0, null, 0, -1); } else { // Altrimenti utilizzo host e port dalla configurazione della connessione standalone From d622034b9e2b098b092a6f0d03809f32d55648d0 Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Fri, 10 Jan 2025 14:27:06 +0100 Subject: [PATCH 10/13] rimosse graffe --- src/SuperCacheManager.php | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/SuperCacheManager.php b/src/SuperCacheManager.php index 4d3466f..2dcc27e 100644 --- a/src/SuperCacheManager.php +++ b/src/SuperCacheManager.php @@ -60,11 +60,11 @@ public function put(string $key, mixed $value, ?int $ttl = null, ?string $connec // Calcola la chiave con o senza namespace in base alla configurazione $finalKey = $this->getFinalKey($key); if ($ttl !== null) { - $this->redis->getRedisConnection($connection_name)->setEx('{' . $finalKey . '}', $ttl, $this->serializeForRedis($value)); + $this->redis->getRedisConnection($connection_name)->setEx($finalKey, $ttl, $this->serializeForRedis($value)); return; } - $this->redis->getRedisConnection($connection_name)->set('{' . $finalKey . '}', $this->serializeForRedis($value)); + $this->redis->getRedisConnection($connection_name)->set($finalKey, $this->serializeForRedis($value)); } public function getTTLKey(string $key, ?string $connection_name = null, bool $isWithTags = false): int @@ -72,7 +72,7 @@ public function getTTLKey(string $key, ?string $connection_name = null, bool $is // Calcola la chiave con o senza namespace in base alla configurazione $finalKey = $this->getFinalKey($key, $isWithTags); - return $this->redis->getRedisConnection($connection_name)->ttl('{' . $finalKey . '}'); + return $this->redis->getRedisConnection($connection_name)->ttl($finalKey); } /** @@ -89,28 +89,28 @@ public function putWithTags(string $key, mixed $value, array $tags, ?int $ttl = $this->redis->pipeline(function ($pipe) use ($finalKey, $finalTags, $value, $tags, $ttl) { // Qui devo mettere le {} perchè così mi assicuro che la chiave e i suoi tags stiano nello stesso has if ($ttl !== null) { - $pipe->setEx('{' . $finalKey . '}', $ttl, $this->serializeForRedis($value)); + $pipe->setEx($finalKey, $ttl, $this->serializeForRedis($value)); } else { - $pipe->set('{' . $finalKey . '}', $this->serializeForRedis($value)); + $pipe->set($finalKey, $this->serializeForRedis($value)); } foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, '{' . $finalKey . '}'); - $pipe->sadd($shard, '{' . $finalKey . '}'); + $shard = $this->getShardNameForTag($tag, $finalKey); + $pipe->sadd($shard, $finalKey); } $pipe->sadd($finalTags, ...$tags); }, $connection_name); } else { if ($ttl !== null) { - $this->redis->getRedisConnection($connection_name)->setEx('{' . $finalKey . '}', $ttl, $this->serializeForRedis($value)); + $this->redis->getRedisConnection($connection_name)->setEx($finalKey, $ttl, $this->serializeForRedis($value)); } else { - $result = $this->redis->getRedisConnection($connection_name)->set('{' . $finalKey . '}', $this->serializeForRedis($value)); + $result = $this->redis->getRedisConnection($connection_name)->set($finalKey, $this->serializeForRedis($value)); } foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, '{' . $finalKey . '}'); - $result = $this->redis->getRedisConnection($connection_name)->sadd($shard, '{' . $finalKey . '}'); + $shard = $this->getShardNameForTag($tag, $finalKey); + $result = $this->redis->getRedisConnection($connection_name)->sadd($shard, $finalKey); } $this->redis->getRedisConnection($connection_name)->sadd($finalTags, ...$tags); @@ -156,7 +156,7 @@ public function get(string $key, ?string $connection_name = null, bool $isWithTa { $finalKey = $this->getFinalKey($key, $isWithTags); - $value = $this->redis->getRedisConnection($connection_name)->get('{' . $finalKey . '}'); + $value = $this->redis->getRedisConnection($connection_name)->get($finalKey); return $value ? $this->unserializeForRedis($value) : null; } @@ -167,7 +167,7 @@ public function get(string $key, ?string $connection_name = null, bool $isWithTa public function forget(string $key, ?string $connection_name = null, bool $isFinalKey = false, bool $isWithTags = false, bool $onlyTags = false): void { if ($isFinalKey) { - $finalKey = str_replace(['{', '}'], '', $key); + $finalKey = $key; } else { $finalKey = $this->getFinalKey($key, $isWithTags); } @@ -178,24 +178,24 @@ public function forget(string $key, ?string $connection_name = null, bool $isFin if (!$isCluster) { $this->redis->pipeline(function ($pipe) use ($isWithTags, $onlyTags, $tags, $finalKey, $finalTags) { foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, '{' . $finalKey . '}'); - $pipe->srem($shard, '{' . $finalKey . '}'); + $shard = $this->getShardNameForTag($tag, $finalKey); + $pipe->srem($shard, $finalKey); } $pipe->del($finalTags); if (!$onlyTags) { - $pipe->del('{' . $finalKey . '}'); + $pipe->del($finalKey); } }, $connection_name); } else { foreach ($tags as $tag) { - $shard = $this->getShardNameForTag($tag, '{' . $finalKey . '}'); - $this->redis->getRedisConnection($connection_name)->srem($shard, '{' . $finalKey . '}'); + $shard = $this->getShardNameForTag($tag, $finalKey); + $this->redis->getRedisConnection($connection_name)->srem($shard, $finalKey); } $this->redis->getRedisConnection($connection_name)->del($finalTags); if (!$onlyTags) { - $this->redis->getRedisConnection($connection_name)->del('{' . $finalKey . '}'); + $this->redis->getRedisConnection($connection_name)->del($finalKey); } } } @@ -275,7 +275,7 @@ public function getFinalKey(string $key, bool $isWithTags = false): string public function getFinalTag(string $finalKey): string { - return '{' . $finalKey . '}:tags'; + return $finalKey . ':tags'; } /** @@ -292,12 +292,12 @@ public function flush(?string $connection_name = null): void public function has(string $key, ?string $connection_name = null, bool $isWithTags = false, bool $isfinalKey = false): bool { if ($isfinalKey) { - $finalKey = str_replace(['{', '}'], '', $key); + $finalKey = $key; } else { $finalKey = $this->getFinalKey($key, $isWithTags); } - return $this->redis->getRedisConnection($connection_name)->exists('{' . $finalKey . '}') > 0; + return $this->redis->getRedisConnection($connection_name)->exists($finalKey) > 0; } /** @@ -367,7 +367,7 @@ public function getKeys(array $patterns, ?string $connection_name = null): array public function getOriginalKey(string $finalKey): string { - $originalKey = str_replace([config('database.redis.options')['prefix'], $this->prefix, '{', '}'], '', $finalKey); + $originalKey = str_replace([config('database.redis.options')['prefix'], $this->prefix], '', $finalKey); if (!$this->useNamespace) { return $originalKey; } From 2922e71d7834870f96c6642357223ed7f77fc164 Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Fri, 10 Jan 2025 14:44:41 +0100 Subject: [PATCH 11/13] rimosse graffe --- src/SuperCacheManager.php | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/SuperCacheManager.php b/src/SuperCacheManager.php index 2dcc27e..26cb3a8 100644 --- a/src/SuperCacheManager.php +++ b/src/SuperCacheManager.php @@ -82,11 +82,10 @@ public function getTTLKey(string $key, ?string $connection_name = null, bool $is public function putWithTags(string $key, mixed $value, array $tags, ?int $ttl = null, ?string $connection_name = null): void { $finalKey = $this->getFinalKey($key, true); - $finalTags = $this->getFinalTag($finalKey); // Usa pipeline solo se non è un cluster $isCluster = config('database.redis.clusters.' . ($connection_name ?? 'default')) !== null; if (!$isCluster) { - $this->redis->pipeline(function ($pipe) use ($finalKey, $finalTags, $value, $tags, $ttl) { + $this->redis->pipeline(function ($pipe) use ($finalKey, $value, $tags, $ttl) { // Qui devo mettere le {} perchè così mi assicuro che la chiave e i suoi tags stiano nello stesso has if ($ttl !== null) { $pipe->setEx($finalKey, $ttl, $this->serializeForRedis($value)); @@ -99,7 +98,7 @@ public function putWithTags(string $key, mixed $value, array $tags, ?int $ttl = $pipe->sadd($shard, $finalKey); } - $pipe->sadd($finalTags, ...$tags); + $pipe->sadd($this->prefix . 'tags:' . $finalKey, ...$tags); }, $connection_name); } else { if ($ttl !== null) { @@ -113,7 +112,7 @@ public function putWithTags(string $key, mixed $value, array $tags, ?int $ttl = $result = $this->redis->getRedisConnection($connection_name)->sadd($shard, $finalKey); } - $this->redis->getRedisConnection($connection_name)->sadd($finalTags, ...$tags); + $this->redis->getRedisConnection($connection_name)->sadd($this->prefix . 'tags:' . $finalKey, ...$tags); } } @@ -171,18 +170,17 @@ public function forget(string $key, ?string $connection_name = null, bool $isFin } else { $finalKey = $this->getFinalKey($key, $isWithTags); } - $finalTags = $this->getFinalTag($finalKey); // Recupera i tag associati alla chiave - $tags = $this->redis->getRedisConnection($connection_name)->smembers($finalTags); + $tags = $this->redis->getRedisConnection($connection_name)->smembers($this->prefix . 'tags:' . $finalKey); $isCluster = config('database.redis.clusters.' . ($connection_name ?? 'default')) !== null; if (!$isCluster) { - $this->redis->pipeline(function ($pipe) use ($isWithTags, $onlyTags, $tags, $finalKey, $finalTags) { + $this->redis->pipeline(function ($pipe) use ($isWithTags, $onlyTags, $tags, $finalKey) { foreach ($tags as $tag) { $shard = $this->getShardNameForTag($tag, $finalKey); $pipe->srem($shard, $finalKey); } - $pipe->del($finalTags); + $pipe->del($this->prefix . 'tags:' . $finalKey); if (!$onlyTags) { $pipe->del($finalKey); } @@ -193,7 +191,7 @@ public function forget(string $key, ?string $connection_name = null, bool $isFin $this->redis->getRedisConnection($connection_name)->srem($shard, $finalKey); } - $this->redis->getRedisConnection($connection_name)->del($finalTags); + $this->redis->getRedisConnection($connection_name)->del($this->prefix . 'tags:' . $finalKey); if (!$onlyTags) { $this->redis->getRedisConnection($connection_name)->del($finalKey); } @@ -219,9 +217,7 @@ public function flushByTags(array $tags, ?string $connection_name = null): void public function getTagsOfKey(string $key, ?string $connection_name = null): array { $finalKey = $this->getFinalKey($key, true); - $finalTags = $this->getFinalTag($finalKey); - - return $this->redis->getRedisConnection($connection_name)->smembers($finalTags); + return $this->redis->getRedisConnection($connection_name)->smembers($this->prefix . 'tags:' . $finalKey); } /** @@ -266,16 +262,11 @@ public function getFinalKey(string $key, bool $isWithTags = false): string if ($this->useNamespace) { $namespace = $this->calculateNamespace($key); - return $this->prefix . $key . ':' . ($isWithTags ? 'byTags:' : '') . $namespace; + return $this->prefix . $key . ':' . $namespace; } // Se il namespace è disabilitato, usa la chiave senza suffisso - return $this->prefix . $key . ($isWithTags ? ':byTags' : ''); - } - - public function getFinalTag(string $finalKey): string - { - return $finalKey . ':tags'; + return $this->prefix . $key; } /** From 211dd6188c21c8998bf0a7f4073b42ec39a98fcd Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Fri, 10 Jan 2025 14:51:16 +0100 Subject: [PATCH 12/13] rimosse graffe --- src/SuperCacheManager.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/SuperCacheManager.php b/src/SuperCacheManager.php index 26cb3a8..d259c21 100644 --- a/src/SuperCacheManager.php +++ b/src/SuperCacheManager.php @@ -109,7 +109,7 @@ public function putWithTags(string $key, mixed $value, array $tags, ?int $ttl = foreach ($tags as $tag) { $shard = $this->getShardNameForTag($tag, $finalKey); - $result = $this->redis->getRedisConnection($connection_name)->sadd($shard, $finalKey); + $this->redis->getRedisConnection($connection_name)->sadd($shard, $finalKey); } $this->redis->getRedisConnection($connection_name)->sadd($this->prefix . 'tags:' . $finalKey, ...$tags); @@ -174,16 +174,14 @@ public function forget(string $key, ?string $connection_name = null, bool $isFin $tags = $this->redis->getRedisConnection($connection_name)->smembers($this->prefix . 'tags:' . $finalKey); $isCluster = config('database.redis.clusters.' . ($connection_name ?? 'default')) !== null; if (!$isCluster) { - $this->redis->pipeline(function ($pipe) use ($isWithTags, $onlyTags, $tags, $finalKey) { + $this->redis->pipeline(function ($pipe) use ($tags, $finalKey) { foreach ($tags as $tag) { $shard = $this->getShardNameForTag($tag, $finalKey); $pipe->srem($shard, $finalKey); } $pipe->del($this->prefix . 'tags:' . $finalKey); - if (!$onlyTags) { - $pipe->del($finalKey); - } + $pipe->del($finalKey); }, $connection_name); } else { foreach ($tags as $tag) { @@ -192,9 +190,7 @@ public function forget(string $key, ?string $connection_name = null, bool $isFin } $this->redis->getRedisConnection($connection_name)->del($this->prefix . 'tags:' . $finalKey); - if (!$onlyTags) { - $this->redis->getRedisConnection($connection_name)->del($finalKey); - } + $this->redis->getRedisConnection($connection_name)->del($finalKey); } } @@ -217,6 +213,7 @@ public function flushByTags(array $tags, ?string $connection_name = null): void public function getTagsOfKey(string $key, ?string $connection_name = null): array { $finalKey = $this->getFinalKey($key, true); + return $this->redis->getRedisConnection($connection_name)->smembers($this->prefix . 'tags:' . $finalKey); } From 01b33572ab15e49576ec03bee4fe9b9f8dd2cb06 Mon Sep 17 00:00:00 2001 From: janua bisconti Date: Mon, 13 Jan 2025 08:25:39 +0100 Subject: [PATCH 13/13] new tests --- tests/Unit/SuperCacheManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/SuperCacheManagerTest.php b/tests/Unit/SuperCacheManagerTest.php index 1ced9de..3728fda 100644 --- a/tests/Unit/SuperCacheManagerTest.php +++ b/tests/Unit/SuperCacheManagerTest.php @@ -148,7 +148,7 @@ public function test_has(): void $this->assertTrue($this->superCache->has('key1')); $this->assertTrue($this->superCache->has('key2', null, true)); $finalKey = $this->superCache->getFinalKey('key2', true); - $this->assertTrue($this->superCache->has('{' . $finalKey . '}', null, true, true)); + $this->assertTrue($this->superCache->has($finalKey, null, true, true)); $this->assertFalse($this->superCache->has('non_existing_key')); }