Skip to content

Commit 4990778

Browse files
fix(redis): use KEYS command instead of SCAN for pattern matching
Problem: Laravel's PhpRedisConnection has inconsistent behavior between KEYS and SCAN: - KEYS command: Automatically adds Laravel's Redis prefix to patterns - SCAN command: Does NOT add prefix, requires full pattern This caused scanKeys() to return 0 results even when keys existed: - Pattern sent: laravel_database_queue_metrics:jobs:* - SCAN looked for: laravel_database_queue_metrics:jobs:* (no auto-prefix) - KEYS would look for: laravel_database_queue_metrics:jobs:* (auto-prefixed) Solution: - Changed scanKeys() to use KEYS command instead of SCAN - KEYS automatically handles prefix, making behavior consistent - Simplified MetricsQueryService to use manager->key() pattern Note: KEYS is fine for our use case because: 1. We use specific patterns (jobs:*:*:*), not wildcard scans 2. Number of job types is limited (not millions of keys) 3. It's called infrequently (overview endpoint) For production systems with millions of keys, SCAN would be preferred, but that would require handling phpredis prefix manually. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 422005d commit 4990778

File tree

2 files changed

+10
-17
lines changed

2 files changed

+10
-17
lines changed

src/Services/MetricsQueryService.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,14 @@ public function getOverview(): array
9797
// Scan Redis for all job metrics keys and aggregate
9898
$manager = app(\PHPeek\LaravelQueueMetrics\Storage\StorageManager::class);
9999

100-
// Build full pattern including Laravel Redis prefix
101-
$redisPrefix = config('database.redis.options.prefix', '');
102-
$ourPrefix = config('queue-metrics.storage.prefix');
103-
$fullPattern = $redisPrefix . $ourPrefix . ':jobs:*';
100+
// Laravel's Redis connection automatically adds prefix for KEYS command
101+
// So we only use our package prefix, not Laravel's Redis prefix
102+
$pattern = $manager->key('jobs', '*', '*', '*');
104103

105104
$driver = $manager->driver();
106105

107-
// Get all job metrics keys - scanKeys expects the FULL pattern with Laravel prefix
108-
$keys = $driver->scanKeys($fullPattern);
106+
// Get all job metrics keys
107+
$keys = $driver->scanKeys($pattern);
109108

110109
// Sum metrics from all keys
111110
foreach ($keys as $key) {

src/Storage/RedisStorageDriver.php

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,11 @@ public function expire(string $key, int $seconds): bool
140140

141141
public function scanKeys(string $pattern): array
142142
{
143-
$keys = [];
144-
$cursor = '0';
145-
146-
do {
147-
/** @var array{0: string, 1: array<string>} */
148-
$result = $this->redis->scan($cursor, ['match' => $pattern, 'count' => 100]);
149-
[$cursor, $found] = $result;
150-
$keys = array_merge($keys, $found);
151-
} while ($cursor !== '0');
152-
153-
return $keys;
143+
// Use KEYS command instead of SCAN for pattern matching
144+
// Laravel's Redis connection automatically adds prefix to patterns for KEYS
145+
// but NOT for SCAN, causing inconsistent behavior with phpredis
146+
/** @var array<string> */
147+
return $this->redis->command('keys', [$pattern]) ?: [];
154148
}
155149

156150
public function pipeline(callable $callback): void

0 commit comments

Comments
 (0)