option('dry-run'); $execute = (bool) $this->option('execute'); if ($dryRun === $execute) { $this->error('Pass exactly one of --dry-run or --execute.'); return self::FAILURE; } $maxKeys = max(1, (int) $this->option('max-keys')); $currentPrefix = (string) config('database.redis.options.prefix'); $horizonPrefix = (string) config('horizon.prefix'); $this->info(sprintf( '[redis-cleanup-legacy-prefix] current_prefix=%s horizon_prefix=%s max_keys=%d %s', $currentPrefix, $horizonPrefix, $maxKeys, $dryRun ? 'dry-run' : 'execute' )); if (in_array($currentPrefix, self::LEGACY_PREFIXES, true) || in_array($horizonPrefix, self::LEGACY_PREFIXES, true)) { $this->error('Current process still uses a legacy prefix. Aborting.'); return self::FAILURE; } $client = $this->unprefixedClient(); $found = []; $cursor = '0'; do { [$cursor, $keys] = $client->scan($cursor, ['COUNT' => 200, 'MATCH' => '*']); foreach ($keys as $key) { $key = (string) $key; if ($this->isProtected($key)) { continue; } if (! $this->isLegacy($key)) { continue; } $found[] = $key; if (count($found) >= $maxKeys) { $cursor = '0'; break; } } } while ($cursor !== '0' && $cursor !== 0); $this->info('matched='.count($found)); foreach (array_slice($found, 0, 20) as $key) { $this->line('key='.$key); } if (count($found) > 20) { $this->line('... truncated listing'); } $unlinked = 0; if (! $dryRun && $found !== []) { foreach (array_chunk($found, 50) as $chunk) { $unlinked += (int) $client->unlink(...$chunk); } } $this->info('unlinked='.$unlinked); Log::info('[redis-cleanup-legacy-prefix] completed', [ 'matched' => count($found), 'unlinked' => $unlinked, 'dry_run' => $dryRun, ]); return self::SUCCESS; } private function isLegacy(string $key): bool { foreach (self::LEGACY_PREFIXES as $prefix) { if (str_starts_with($key, $prefix)) { return true; } } return false; } private function isProtected(string $key): bool { foreach (self::PROTECTED_PREFIXES as $prefix) { if (str_starts_with($key, $prefix)) { return true; } } return false; } private function unprefixedClient(): PredisClient { $redis = config('database.redis.default', []); return new PredisClient([ 'scheme' => 'tcp', 'host' => $redis['host'] ?? '127.0.0.1', 'port' => (int) ($redis['port'] ?? 6379), 'password' => $redis['password'] ?? null, 'database' => (int) ($redis['database'] ?? 0), ]); } }