Skip to content

Commit 2f58629

Browse files
author
Emmanuel Merali
committed
External key distributor
Added possibility to specify a user land key distributor function.
1 parent 497300a commit 2f58629

File tree

4 files changed

+108
-13
lines changed

4 files changed

+108
-13
lines changed

redis_array.c

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ zend_function_entry redis_array_functions[] = {
4646
PHP_ME(RedisArray, _hosts, NULL, ZEND_ACC_PUBLIC)
4747
PHP_ME(RedisArray, _target, NULL, ZEND_ACC_PUBLIC)
4848
PHP_ME(RedisArray, _function, NULL, ZEND_ACC_PUBLIC)
49+
PHP_ME(RedisArray, _distributor, NULL, ZEND_ACC_PUBLIC)
4950
PHP_ME(RedisArray, _rehash, NULL, ZEND_ACC_PUBLIC)
5051

5152
/* special implementation for a few functions */
@@ -92,6 +93,12 @@ void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
9293
efree(ra->z_fun);
9394
}
9495

96+
/* delete distributor */
97+
if(ra->z_dist) {
98+
zval_dtor(ra->z_dist);
99+
efree(ra->z_dist);
100+
}
101+
95102
/* delete list of pure commands */
96103
zval_dtor(ra->z_pure_cmds);
97104
efree(ra->z_pure_cmds);
@@ -178,7 +185,7 @@ uint32_t rcrc32(const char *s, size_t sz) {
178185
Public constructor */
179186
PHP_METHOD(RedisArray, __construct)
180187
{
181-
zval *z0, *z_fun = NULL, **zpData, *z_opts = NULL;
188+
zval *z0, *z_fun = NULL, *z_dist = NULL, **zpData, *z_opts = NULL;
182189
char *name = NULL;
183190
int id;
184191
RedisArray *ra = NULL;
@@ -208,6 +215,13 @@ PHP_METHOD(RedisArray, __construct)
208215
zval_copy_ctor(z_fun);
209216
}
210217

218+
/* extract function name. */
219+
if(FAILURE != zend_hash_find(hOpts, "distributor", sizeof("distributor"), (void**)&zpData)) {
220+
MAKE_STD_ZVAL(z_dist);
221+
*z_dist = **zpData;
222+
zval_copy_ctor(z_dist);
223+
}
224+
211225
/* extract index option. */
212226
if(FAILURE != zend_hash_find(hOpts, "index", sizeof("index"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) {
213227
b_index = Z_BVAL_PP(zpData);
@@ -226,7 +240,7 @@ PHP_METHOD(RedisArray, __construct)
226240
break;
227241

228242
case IS_ARRAY:
229-
ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, hPrev, b_index TSRMLS_CC);
243+
ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index TSRMLS_CC);
230244
break;
231245

232246
default:
@@ -428,6 +442,29 @@ PHP_METHOD(RedisArray, _function)
428442
}
429443
}
430444

445+
PHP_METHOD(RedisArray, _distributor)
446+
{
447+
zval *object;
448+
int i;
449+
RedisArray *ra;
450+
451+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
452+
&object, redis_array_ce) == FAILURE) {
453+
RETURN_FALSE;
454+
}
455+
456+
if (redis_array_get(object, &ra TSRMLS_CC) < 0) {
457+
RETURN_FALSE;
458+
}
459+
460+
if(ra->z_fun) {
461+
*return_value = *ra->z_fun;
462+
zval_copy_ctor(return_value);
463+
} else {
464+
RETURN_NULL();
465+
}
466+
}
467+
431468
PHP_METHOD(RedisArray, _rehash)
432469
{
433470
zval *object, *z_cb = NULL;

redis_array.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ PHP_METHOD(RedisArray, __call);
1111
PHP_METHOD(RedisArray, _hosts);
1212
PHP_METHOD(RedisArray, _target);
1313
PHP_METHOD(RedisArray, _function);
14+
PHP_METHOD(RedisArray, _distributor);
1415
PHP_METHOD(RedisArray, _rehash);
1516

1617
PHP_METHOD(RedisArray, info);
@@ -36,6 +37,7 @@ typedef struct RedisArray_ {
3637
zend_bool index; /* use per-node index */
3738
zend_bool auto_rehash; /* migrate keys on read operations */
3839
zval *z_fun; /* key extractor, callable */
40+
zval *z_dist; /* key distributor, callable */
3941
zval *z_pure_cmds; /* hash table */
4042

4143
struct RedisArray_ *prev;

redis_array_impl.c

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
150150

151151
zval *z_params_hosts, **z_hosts;
152152
zval *z_params_prev, **z_prev;
153-
zval *z_params_funs, **z_data_pp, *z_fun = NULL;
153+
zval *z_params_funs, **z_data_pp, *z_fun = NULL, *z_dist = NULL;
154154
zval *z_params_index;
155155
zval *z_params_autorehash;
156156
RedisArray *ra = NULL;
@@ -188,6 +188,16 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
188188
zval_copy_ctor(z_fun);
189189
}
190190

191+
/* find distributor */
192+
MAKE_STD_ZVAL(z_params_funs);
193+
array_init(z_params_funs);
194+
sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.distributor")), z_params_funs TSRMLS_CC);
195+
if (zend_hash_find(Z_ARRVAL_P(z_params_funs), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) {
196+
MAKE_STD_ZVAL(z_dist);
197+
*z_dist = **z_data_pp;
198+
zval_copy_ctor(z_dist);
199+
}
200+
191201
/* find index option */
192202
MAKE_STD_ZVAL(z_params_index);
193203
array_init(z_params_index);
@@ -209,7 +219,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
209219
}
210220

211221
/* create RedisArray object */
212-
ra = ra_make_array(hHosts, z_fun, hPrev, b_index TSRMLS_CC);
222+
ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index TSRMLS_CC);
213223
ra->auto_rehash = b_autorehash;
214224

215225
/* cleanup */
@@ -228,7 +238,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
228238
}
229239

230240
RedisArray *
231-
ra_make_array(HashTable *hosts, zval *z_fun, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC) {
241+
ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC) {
232242

233243
int count = zend_hash_num_elements(hosts);
234244

@@ -238,6 +248,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, HashTable *hosts_prev, zend_bool b_
238248
ra->redis = emalloc(count * sizeof(zval*));
239249
ra->count = count;
240250
ra->z_fun = NULL;
251+
ra->z_dist = NULL;
241252
ra->z_multi_exec = NULL;
242253
ra->index = b_index;
243254
ra->auto_rehash = 0;
@@ -248,7 +259,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, HashTable *hosts_prev, zend_bool b_
248259
if(NULL == ra_load_hosts(ra, hosts TSRMLS_CC)) {
249260
return NULL;
250261
}
251-
ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, NULL, b_index TSRMLS_CC) : NULL;
262+
ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index TSRMLS_CC) : NULL;
252263

253264
/* copy function if provided */
254265
if(z_fun) {
@@ -257,6 +268,13 @@ ra_make_array(HashTable *hosts, zval *z_fun, HashTable *hosts_prev, zend_bool b_
257268
zval_copy_ctor(ra->z_fun);
258269
}
259270

271+
/* copy distributor if provided */
272+
if(z_dist) {
273+
MAKE_STD_ZVAL(ra->z_dist);
274+
*ra->z_dist = *z_dist;
275+
zval_copy_ctor(ra->z_dist);
276+
}
277+
260278
return ra;
261279
}
262280

@@ -322,6 +340,37 @@ ra_extract_key(RedisArray *ra, const char *key, int key_len, int *out_len TSRMLS
322340
return out;
323341
}
324342

343+
/* call userland key distributor function */
344+
zend_bool
345+
ra_call_distributor(RedisArray *ra, const char *key, int key_len, int *pos TSRMLS_DC) {
346+
347+
char *error = NULL;
348+
zval z_ret;
349+
zval *z_argv0;
350+
351+
/* check that we can call the extractor function */
352+
if(!zend_is_callable_ex(ra->z_dist, NULL, 0, NULL, NULL, NULL, &error TSRMLS_CC)) {
353+
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not call distributor function");
354+
return 0;
355+
}
356+
//convert_to_string(ra->z_fun);
357+
358+
/* call extraction function */
359+
MAKE_STD_ZVAL(z_argv0);
360+
ZVAL_STRINGL(z_argv0, key, key_len, 0);
361+
call_user_function(EG(function_table), NULL, ra->z_dist, &z_ret, 1, &z_argv0 TSRMLS_CC);
362+
efree(z_argv0);
363+
364+
if(Z_TYPE(z_ret) != IS_LONG) {
365+
zval_dtor(&z_ret);
366+
return 0;
367+
}
368+
369+
*pos = Z_LVAL(z_ret);
370+
zval_dtor(&z_ret);
371+
return 1;
372+
}
373+
325374
zval *
326375
ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC) {
327376

@@ -334,12 +383,19 @@ ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_D
334383
if(!out)
335384
return NULL;
336385

337-
/* hash */
338-
hash = rcrc32(out, out_len);
339-
efree(out);
340-
341-
/* get position on ring */
342-
pos = (int)((((uint64_t)hash) * ra->count) / 0xffffffff);
386+
if(ra->z_dist) {
387+
if (!ra_call_distributor(ra, key, key_len, &pos TSRMLS_CC)) {
388+
return NULL;
389+
}
390+
}
391+
else {
392+
/* hash */
393+
hash = rcrc32(out, out_len);
394+
efree(out);
395+
396+
/* get position on ring */
397+
pos = (int)((((uint64_t)hash) * ra->count) / 0xffffffff);
398+
}
343399
if(out_pos) *out_pos = pos;
344400

345401
return ra->redis[pos];

redis_array_impl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
RedisArray* ra_load_hosts(RedisArray *ra, HashTable *hosts TSRMLS_DC);
99
RedisArray *ra_load_array(const char *name TSRMLS_DC);
10-
RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC);
10+
RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index TSRMLS_DC);
1111
zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC);
1212
zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC);
1313
void ra_init_function_table(RedisArray *ra);

0 commit comments

Comments
 (0)