Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e80600e

Browse files
authored
Issue #548 (#1649)
Adds `Redis::SCAN_PREFIX` and `Redis::SCAN_NOPREFIX` as options to SCAN. See #548
1 parent 508fef6 commit e80600e

File tree

8 files changed

+130
-13
lines changed

8 files changed

+130
-13
lines changed

README.markdown

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,11 @@ $redis->setOption(Redis::OPT_PREFIX, 'myAppName:'); // use custom prefix on all
354354
*/
355355
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
356356
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
357+
358+
/* Scan can also be configured to automatically prepend the currently set PhpRedis
359+
prefix to any MATCH pattern. */
360+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);
361+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);
357362
~~~
358363

359364

common.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ typedef enum {
103103

104104
/* SCAN options */
105105
#define REDIS_SCAN_NORETRY 0
106-
#define REDIS_SCAN_RETRY 1
106+
#define REDIS_SCAN_RETRY 1
107+
#define REDIS_SCAN_PREFIX 2
108+
#define REDIS_SCAN_NOPREFIX 3
107109

108110
/* GETBIT/SETBIT offset range limits */
109111
#define BITOP_MIN_OFFSET 0

library.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ int
722722
redis_cmd_append_sstr_dbl(smart_string *str, double value)
723723
{
724724
char tmp[64];
725-
int len, retval;
725+
int len;
726726

727727
/* Convert to string */
728728
len = snprintf(tmp, sizeof(tmp), "%.16g", value);
@@ -1785,7 +1785,7 @@ redis_sock_create(char *host, int host_len, int port,
17851785

17861786
redis_sock->err = NULL;
17871787

1788-
redis_sock->scan = REDIS_SCAN_NORETRY;
1788+
redis_sock->scan = 0;
17891789

17901790
redis_sock->readonly = 0;
17911791
redis_sock->tcp_keepalive = 0;

redis.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,8 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster) {
723723
zend_declare_class_constant_long(ce, ZEND_STRL("OPT_SCAN"), REDIS_OPT_SCAN);
724724
zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_RETRY"), REDIS_SCAN_RETRY);
725725
zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NORETRY"), REDIS_SCAN_NORETRY);
726+
zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_PREFIX"), REDIS_SCAN_PREFIX);
727+
zend_declare_class_constant_long(ce, ZEND_STRL("SCAN_NOPREFIX"), REDIS_SCAN_NOPREFIX);
726728

727729
/* Cluster option to allow for slave failover */
728730
if (is_cluster) {
@@ -3462,7 +3464,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
34623464
RedisSock *redis_sock;
34633465
HashTable *hash;
34643466
char *pattern = NULL, *cmd, *key = NULL;
3465-
int cmd_len, num_elements, key_free = 0;
3467+
int cmd_len, num_elements, key_free = 0, pattern_free = 0;
34663468
size_t key_len = 0, pattern_len = 0;
34673469
zend_string *match_type = NULL;
34683470
zend_long count = 0, iter;
@@ -3520,6 +3522,10 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
35203522
key_free = redis_key_prefix(redis_sock, &key, &key_len);
35213523
}
35223524

3525+
if (redis_sock->scan & REDIS_SCAN_PREFIX) {
3526+
pattern_free = redis_key_prefix(redis_sock, &pattern, &pattern_len);
3527+
}
3528+
35233529
/**
35243530
* Redis can return to us empty keys, especially in the case where there
35253531
* are a large number of keys to scan, and we're matching against a
@@ -3552,9 +3558,12 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
35523558
/* Get the number of elements */
35533559
hash = Z_ARRVAL_P(return_value);
35543560
num_elements = zend_hash_num_elements(hash);
3555-
} while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 &&
3561+
} while (redis_sock->scan & REDIS_SCAN_RETRY && iter != 0 &&
35563562
num_elements == 0);
35573563

3564+
/* Free our pattern if it was prefixed */
3565+
if (pattern_free) efree(pattern);
3566+
35583567
/* Free our key if it was prefixed */
35593568
if(key_free) efree(key);
35603569

redis_cluster.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,7 +2411,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,
24112411
{
24122412
redisCluster *c = GET_CONTEXT();
24132413
char *cmd, *pat = NULL, *key = NULL;
2414-
size_t key_len = 0, pat_len = 0;
2414+
size_t key_len = 0, pat_len = 0, pat_free = 0;
24152415
int cmd_len, key_free = 0;
24162416
short slot;
24172417
zval *z_it;
@@ -2450,6 +2450,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,
24502450
key_free = redis_key_prefix(c->flags, &key, &key_len);
24512451
slot = cluster_hash_key(key, key_len);
24522452

2453+
if (c->flags->scan & REDIS_SCAN_PREFIX) {
2454+
pat_free = redis_key_prefix(c->flags, &pat, &pat_len);
2455+
}
2456+
24532457
// If SCAN_RETRY is set, loop until we get a zero iterator or until
24542458
// we get non-zero elements. Otherwise we just send the command once.
24552459
do {
@@ -2488,7 +2492,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS,
24882492

24892493
// Free our command
24902494
efree(cmd);
2491-
} while (c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
2495+
} while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
2496+
2497+
// Free our pattern
2498+
if (pat_free) efree(pat);
24922499

24932500
// Free our key
24942501
if (key_free) efree(key);
@@ -2505,7 +2512,7 @@ PHP_METHOD(RedisCluster, scan) {
25052512
int cmd_len;
25062513
short slot;
25072514
zval *z_it, *z_node;
2508-
long it, num_ele;
2515+
long it, num_ele, pat_free = 0;
25092516
zend_long count = 0;
25102517

25112518
/* Treat as read-only */
@@ -2534,6 +2541,10 @@ PHP_METHOD(RedisCluster, scan) {
25342541
RETURN_FALSE;
25352542
}
25362543

2544+
if (c->flags->scan & REDIS_SCAN_PREFIX) {
2545+
pat_free = redis_key_prefix(c->flags, &pat, &pat_len);
2546+
}
2547+
25372548
/* With SCAN_RETRY on, loop until we get some keys, otherwise just return
25382549
* what Redis does, as it does */
25392550
do {
@@ -2570,7 +2581,9 @@ PHP_METHOD(RedisCluster, scan) {
25702581
efree(cmd);
25712582

25722583
num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value));
2573-
} while (c->flags->scan == REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
2584+
} while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0);
2585+
2586+
if (pat_free) efree(pat);
25742587

25752588
Z_LVAL_P(z_it) = it;
25762589
}

redis_commands.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4032,11 +4032,16 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS,
40324032
RETURN_TRUE;
40334033
case REDIS_OPT_SCAN:
40344034
val_long = zval_get_long(val);
4035-
if (val_long==REDIS_SCAN_NORETRY || val_long==REDIS_SCAN_RETRY) {
4036-
redis_sock->scan = val_long;
4037-
RETURN_TRUE;
4035+
if (val_long == REDIS_SCAN_NORETRY) {
4036+
redis_sock->scan &= ~REDIS_SCAN_RETRY;
4037+
} else if (val_long == REDIS_SCAN_NOPREFIX) {
4038+
redis_sock->scan &= ~REDIS_SCAN_PREFIX;
4039+
} else if (val_long == REDIS_SCAN_RETRY || val_long == REDIS_SCAN_PREFIX) {
4040+
redis_sock->scan |= val_long;
4041+
} else {
4042+
break;
40384043
}
4039-
break;
4044+
RETURN_TRUE;
40404045
case REDIS_OPT_FAILOVER:
40414046
val_long = zval_get_long(val);
40424047
if (val_long == REDIS_FAILOVER_NONE ||

tests/RedisClusterTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,55 @@ public function testScan() {
222222
$this->assertEquals($i_scan_count, $i_key_count);
223223
}
224224

225+
public function testScanPrefix() {
226+
$arr_prefixes = ['prefix-a:', 'prefix-b:'];
227+
$str_id = uniqid();
228+
229+
$arr_keys = [];
230+
foreach ($arr_prefixes as $str_prefix) {
231+
$this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
232+
$this->redis->set($str_id, "LOLWUT");
233+
$arr_keys[$str_prefix] = $str_id;
234+
}
235+
236+
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
237+
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);
238+
239+
foreach ($arr_prefixes as $str_prefix) {
240+
$arr_prefix_keys = [];
241+
$this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
242+
243+
foreach ($this->redis->_masters() as $arr_master) {
244+
$it = NULL;
245+
while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) {
246+
foreach ($arr_iter as $str_key) {
247+
$arr_prefix_keys[$str_prefix] = $str_key;
248+
}
249+
}
250+
}
251+
252+
$this->assertTrue(count($arr_prefix_keys) == 1 && isset($arr_prefix_keys[$str_prefix]));
253+
}
254+
255+
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);
256+
257+
$arr_scan_keys = [];
258+
259+
foreach ($this->redis->_masters() as $arr_master) {
260+
$it = NULL;
261+
while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) {
262+
foreach ($arr_iter as $str_key) {
263+
$arr_scan_keys[] = $str_key;
264+
}
265+
}
266+
}
267+
268+
/* We should now have both prefixs' keys */
269+
foreach ($arr_keys as $str_prefix => $str_id) {
270+
$this->assertTrue(in_array("${str_prefix}${str_id}", $arr_scan_keys));
271+
}
272+
}
273+
225274
// Run some simple tests against the PUBSUB command. This is problematic, as we
226275
// can't be sure what's going on in the instance, but we can do some things.
227276
public function testPubSub() {

tests/RedisTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5008,7 +5008,41 @@ public function testScan() {
50085008
}
50095009
}
50105010
}
5011+
}
5012+
5013+
public function testScanPrefix() {
5014+
$keyid = uniqid();
5015+
5016+
/* Set some keys with different prefixes */
5017+
$arr_prefixes = ['prefix-a:', 'prefix-b:'];
5018+
foreach ($arr_prefixes as $str_prefix) {
5019+
$this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
5020+
$this->redis->set("$keyid", "LOLWUT");
5021+
$arr_all_keys["${str_prefix}${keyid}"] = true;
5022+
}
5023+
5024+
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
5025+
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);
5026+
5027+
foreach ($arr_prefixes as $str_prefix) {
5028+
$this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
5029+
$it = NULL;
5030+
$arr_keys = $this->redis->scan($it, "*$keyid*");
5031+
$this->assertEquals($arr_keys, ["${str_prefix}${keyid}"]);
5032+
}
5033+
5034+
/* Unset the prefix option */
5035+
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);
5036+
5037+
$it = NULL;
5038+
while ($arr_keys = $this->redis->scan($it, "*$keyid*")) {
5039+
foreach ($arr_keys as $str_key) {
5040+
unset($arr_all_keys[$str_key]);
5041+
}
5042+
}
50115043

5044+
/* Should have touched every key */
5045+
$this->assertTrue(count($arr_all_keys) == 0);
50125046
}
50135047

50145048
public function testHScan() {

0 commit comments

Comments
 (0)