Skip to content

Commit 1d39d50

Browse files
committed
Fix redis json
1 parent 70388ea commit 1d39d50

9 files changed

Lines changed: 175 additions & 41 deletions

File tree

‎.github/workflows/tests.yml‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ jobs:
1717
ports:
1818
- 11211:11211
1919
redis:
20-
image: redis:6.0
20+
image: redis:8.8
2121
ports:
2222
- 6379:6379
23-
options: --entrypoint redis-server
2423

2524
strategy:
2625
fail-fast: true
@@ -51,11 +50,6 @@ jobs:
5150

5251
tests-cluster:
5352
runs-on: ubuntu-latest
54-
services:
55-
memcached:
56-
image: memcached:1.6-alpine
57-
ports:
58-
- 11211:11211
5953

6054
strategy:
6155
fail-fast: true
@@ -68,13 +62,18 @@ jobs:
6862
- name: Checkout code
6963
uses: actions/checkout@v6
7064

71-
- name: Install Redis Server for Cluster
65+
- name: Install Redis 8.8 for Cluster
7266
run: |
67+
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
68+
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
7369
sudo apt-get update
74-
sudo apt-get install -y redis-server
70+
sudo apt-get install -y redis
7571
7672
- name: Start Redis Cluster
7773
run: |
74+
REJSON=$(dpkg -S rejson.so 2>/dev/null | awk -F': ' '/rejson\.so$/ {print $2; exit}')
75+
[ -n "$REJSON" ] || REJSON=$(find /usr/lib /opt -name rejson.so 2>/dev/null | head -1)
76+
sudo sysctl -w vm.overcommit_memory=1 >/dev/null
7877
BASE_PORT=7000
7978
NODE_COUNT=3
8079
node_addresses=""
@@ -88,9 +87,11 @@ jobs:
8887
cluster-config-file nodes.conf
8988
cluster-node-timeout 5000
9089
appendonly no
90+
databases 1
9191
daemonize yes
9292
bind 127.0.0.1
9393
dir "$(pwd)/redis-cluster/node-$port"
94+
${REJSON:+loadmodule $REJSON}
9495
EOF
9596
redis-server ./redis-cluster/node-$port/redis.conf
9697
done
@@ -111,7 +112,7 @@ jobs:
111112

112113
- name: Install dependencies
113114
run: composer update --prefer-dist --no-interaction --no-progress
114-
- name: Execute cluster tests
115+
- name: Execute Redis cluster tests
115116
env:
116117
PCA_REDIS_0_NODES: '["127.0.0.1:7000","127.0.0.1:7001","127.0.0.1:7002"]'
117-
run: vendor/bin/phpunit
118+
run: vendor/bin/phpunit tests/Dashboards/Redis

‎src/Dashboards/Redis/Compatibility/Cluster/PredisCluster.php‎

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
use Exception;
1212
use InvalidArgumentException;
1313
use Predis\Client as PredisClient;
14+
use Predis\Cluster\RedisStrategy;
1415
use Predis\Collection\Iterator\Keyspace;
1516
use RobiNN\Pca\Dashboards\DashboardException;
1617
use RobiNN\Pca\Dashboards\Redis\Compatibility\RedisCompatibilityInterface;
1718
use RobiNN\Pca\Dashboards\Redis\Compatibility\RedisExtra;
19+
use RuntimeException;
20+
use Throwable;
1821

1922
/**
2023
* @method bool restore(string $key, int $ttl, string $value)
@@ -38,7 +41,7 @@ class PredisCluster extends PredisClient implements RedisCompatibilityInterface
3841
'zset' => 'zset',
3942
'hash' => 'hash',
4043
'stream' => 'stream',
41-
'ReJSON-RL' => 'rejson',
44+
'ReJSON-RL' => 'json',
4245
];
4346

4447
/**
@@ -207,7 +210,7 @@ public function pipelineKeys(array $keys): array {
207210
if (is_array($result) && count($result) >= 3) {
208211
$data[$key] = [
209212
'ttl' => $result[0],
210-
'type' => $result[1],
213+
'type' => $this->data_types[(string) $result[1]] ?? $result[1],
211214
'size' => $result[2] ?? 0,
212215
'count' => isset($result[3]) && is_numeric($result[3]) ? (int) $result[3] : null,
213216
];
@@ -317,4 +320,40 @@ public function commandExists(string $command): bool {
317320
public function restoreKeys(string $key, int $ttl, string $value): bool {
318321
return (string) $this->restore($key, $ttl, $value) === 'OK';
319322
}
323+
324+
/**
325+
* @throws Exception
326+
*/
327+
public function jsonGet(string $key): string {
328+
return (string) $this->json('jsonget', $key, [$key]);
329+
}
330+
331+
/**
332+
* @throws Exception
333+
*/
334+
public function jsonSet(string $key, mixed $path): bool {
335+
return (string) $this->json('jsonset', $key, [$key, '$', $path]) === 'OK';
336+
}
337+
338+
/**
339+
* Predis cannot route JSON.* commands on its own, so the slot is computed from the key and set on the (native) command explicitly.
340+
*
341+
* @param array<int, mixed> $arguments
342+
*
343+
* @throws Exception
344+
*/
345+
private function json(string $id, string $key, array $arguments): mixed {
346+
$command = $this->createCommand($id, $arguments);
347+
$command->setSlot((new RedisStrategy())->getSlotByKey($key));
348+
349+
try {
350+
return $this->executeCommand($command);
351+
} catch (Throwable $e) {
352+
throw new RuntimeException($e->getMessage(), (int) $e->getCode(), $e);
353+
}
354+
}
355+
356+
protected function moduleList(): mixed {
357+
return $this->nodes[0]->executeRaw(['MODULE', 'LIST']);
358+
}
320359
}

‎src/Dashboards/Redis/Compatibility/Cluster/RedisCluster.php‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class RedisCluster extends \RedisCluster implements RedisCompatibilityInterface
3434
Redis::REDIS_ZSET => 'zset',
3535
Redis::REDIS_HASH => 'hash',
3636
Redis::REDIS_STREAM => 'stream',
37-
'ReJSON-RL' => 'rejson',
37+
'ReJSON-RL' => 'json',
3838
];
3939

4040
/**
@@ -184,7 +184,7 @@ public function pipelineKeys(array $keys): array {
184184
if (is_array($results) && count($results) >= 3) {
185185
$data[$key] = [
186186
'ttl' => $results[0],
187-
'type' => $results[1],
187+
'type' => $this->data_types[(string) $results[1]] ?? $results[1],
188188
'size' => $results[2] ?? 0,
189189
'count' => isset($results[3]) && is_numeric($results[3]) ? (int) $results[3] : null,
190190
];
@@ -310,4 +310,27 @@ public function commandExists(string $command): bool {
310310
public function restoreKeys(string $key, int $ttl, string $value): bool {
311311
return $this->restore($key, $ttl, $value);
312312
}
313+
314+
/**
315+
* @throws RedisClusterException
316+
*/
317+
public function jsonGet(string $key): string {
318+
return (string) $this->rawcommand($key, 'JSON.GET', $key);
319+
}
320+
321+
/**
322+
* @throws RedisClusterException
323+
*/
324+
public function jsonSet(string $key, mixed $value): bool {
325+
$raw = $this->rawcommand($key, 'JSON.SET', $key, '$', $value);
326+
327+
return $raw === true || $raw === 'OK';
328+
}
329+
330+
/**
331+
* @throws RedisClusterException
332+
*/
333+
protected function moduleList(): mixed {
334+
return $this->rawcommand($this->nodes[0], 'MODULE', 'LIST');
335+
}
313336
}

‎src/Dashboards/Redis/Compatibility/Predis.php‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use Exception;
1212
use Predis\Client;
1313
use Predis\Collection\Iterator\Keyspace;
14+
use RuntimeException;
15+
use Throwable;
1416

1517
/**
1618
* @method bool restore(string $key, int $ttl, string $value)
@@ -30,7 +32,7 @@ class Predis extends Client implements RedisCompatibilityInterface {
3032
'zset' => 'zset',
3133
'hash' => 'hash',
3234
'stream' => 'stream',
33-
'ReJSON-RL' => 'rejson',
35+
'ReJSON-RL' => 'json',
3436
];
3537

3638
/**
@@ -159,7 +161,7 @@ public function pipelineKeys(array $keys): array {
159161

160162
$data[$key] = [
161163
'ttl' => $result[0],
162-
'type' => $result[1],
164+
'type' => $this->data_types[(string) $result[1]] ?? $result[1],
163165
'size' => $result[2] ?? 0,
164166
'count' => isset($result[3]) && is_numeric($result[3]) ? (int) $result[3] : null,
165167
];
@@ -206,4 +208,33 @@ public function commandExists(string $command): bool {
206208
public function restoreKeys(string $key, int $ttl, string $value): bool {
207209
return (string) $this->restore($key, $ttl, $value) === 'OK';
208210
}
211+
212+
/**
213+
* @throws Exception
214+
*/
215+
public function jsonGet(string $key): string {
216+
return (string) $this->json('jsonget', [$key]);
217+
}
218+
219+
/**
220+
* @throws Exception
221+
*/
222+
public function jsonSet(string $key, mixed $path): bool {
223+
return (string) $this->json('jsonset', [$key, '$', $path]) === 'OK';
224+
}
225+
226+
/**
227+
* Run a native JSON command.
228+
*
229+
* @param array<int, mixed> $arguments
230+
*
231+
* @throws Exception
232+
*/
233+
private function json(string $id, array $arguments): mixed {
234+
try {
235+
return $this->executeCommand($this->createCommand($id, $arguments));
236+
} catch (Throwable $e) {
237+
throw new RuntimeException($e->getMessage(), (int) $e->getCode(), $e);
238+
}
239+
}
209240
}

‎src/Dashboards/Redis/Compatibility/Redis.php‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class Redis extends \Redis implements RedisCompatibilityInterface {
2525
self::REDIS_ZSET => 'zset',
2626
self::REDIS_HASH => 'hash',
2727
self::REDIS_STREAM => 'stream',
28-
'ReJSON-RL' => 'rejson',
28+
'ReJSON-RL' => 'json',
2929
];
3030

3131
/**
@@ -184,7 +184,7 @@ public function pipelineKeys(array $keys): array {
184184

185185
$data[$key] = [
186186
'ttl' => $result[0],
187-
'type' => $result[1],
187+
'type' => $this->data_types[(string) $result[1]] ?? $result[1],
188188
'size' => $result[2] ?? 0,
189189
'count' => isset($result[3]) && is_numeric($result[3]) ? (int) $result[3] : null,
190190
];
@@ -231,4 +231,20 @@ public function commandExists(string $command): bool {
231231
public function restoreKeys(string $key, int $ttl, string $value): bool {
232232
return $this->restore($key, $ttl, $value);
233233
}
234+
235+
/**
236+
* @throws RedisException
237+
*/
238+
public function jsonGet(string $key): string {
239+
return (string) $this->rawcommand('JSON.GET', $key);
240+
}
241+
242+
/**
243+
* @throws RedisException
244+
*/
245+
public function jsonSet(string $key, mixed $value): bool {
246+
$raw = $this->rawcommand('JSON.SET', $key, '$', $value);
247+
248+
return $raw === true || $raw === 'OK';
249+
}
234250
}

‎src/Dashboards/Redis/Compatibility/RedisExtra.php‎

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,6 @@ public function parseSectionData(string $section): array {
6868
}, $info);
6969
}
7070

71-
/**
72-
* @throws Exception
73-
*/
74-
public function jsonGet(string $key): string {
75-
return $this->rawCommand('JSON.GET', $key);
76-
}
77-
78-
/**
79-
* @throws Exception
80-
*/
81-
public function jsonSet(string $key, mixed $value): bool {
82-
$raw = $this->rawCommand('JSON.SET', $key, '$', $value);
83-
84-
return $raw === true || $raw === 'OK';
85-
}
86-
8771
/**
8872
* @return array<int, array<string, int|string>>
8973
*
@@ -99,7 +83,7 @@ public function getModules(): array {
9983
$modules = [];
10084

10185
try {
102-
$list = $this->rawCommand('MODULE', 'LIST'); // require Redis >= 4.0
86+
$list = $this->moduleList(); // require Redis >= 4.0
10387
} catch (Exception) {
10488
return [];
10589
}
@@ -118,6 +102,13 @@ public function getModules(): array {
118102
return $modules;
119103
}
120104

105+
/**
106+
* @throws Exception
107+
*/
108+
protected function moduleList(): mixed {
109+
return $this->rawCommand('MODULE', 'LIST');
110+
}
111+
121112
/**
122113
* @throws Exception
123114
*/

‎src/Dashboards/Redis/RedisTypes.php‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function getAllTypes(): array {
2727
$exclude = ['none', 'other'];
2828

2929
if (!$this->redis->checkModule('ReJSON')) {
30-
$exclude[] = 'rejson';
30+
$exclude[] = 'json';
3131
}
3232

3333
foreach ($this->redis->data_types as $type) {
@@ -104,7 +104,7 @@ private function getKeyValue(string $type, string $key): array {
104104
$value = $ranges[$stream_id] ?? [];
105105
$value = json_encode($value, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
106106
break;
107-
case 'rejson':
107+
case 'json':
108108
$value = $this->redis->jsonGet($key);
109109
break;
110110
default:
@@ -131,7 +131,7 @@ public function getAllKeyValues(string $type, string $key): array|string {
131131
'zset' => $this->redis->zRange($key, 0, -1),
132132
'hash' => $this->redis->hGetAll($key),
133133
'stream' => $this->redis->xRange($key, '-', '+'),
134-
'rejson' => $this->redis->jsonGet($key),
134+
'json' => $this->redis->jsonGet($key),
135135
default => '',
136136
};
137137
}
@@ -202,7 +202,7 @@ public function store(string $type, string $key, string $value, string $old_valu
202202

203203
$this->redis->streamAdd($key, $options['stream_id'], $fields);
204204
break;
205-
case 'rejson':
205+
case 'json':
206206
$this->redis->jsonSet($key, $value);
207207
break;
208208
default:

‎templates/components/macros.twig‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
'list': 'orange',
2828
'zset': 'red',
2929
'hash': 'purple',
30-
'stream': 'indigo'
30+
'stream': 'indigo',
31+
'json': 'blue',
3132
} %}
3233

3334
{{ _self.badge(type, type_colors[type] ?? 'blue', 'uppercase') }}

0 commit comments

Comments
 (0)