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

Skip to content

Commit 5874b07

Browse files
committed
Allow mixing multi and pipeline modes
1 parent a56ed7f commit 5874b07

3 files changed

Lines changed: 114 additions & 41 deletions

File tree

common.h

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,17 @@ typedef enum _PUBSUB_TYPE {
468468
#define BITOP_MIN_OFFSET 0
469469
#define BITOP_MAX_OFFSET 4294967295U
470470

471+
/* Transaction modes */
472+
#define ATOMIC 0
473+
#define MULTI 1
474+
#define PIPELINE 2
475+
471476
#define IF_ATOMIC() if (redis_sock->mode == ATOMIC)
472477
#define IF_NOT_ATOMIC() if (redis_sock->mode != ATOMIC)
473-
#define IF_MULTI() if (redis_sock->mode == MULTI)
474-
#define IF_NOT_MULTI() if (redis_sock->mode != MULTI)
475-
#define IF_PIPELINE() if (redis_sock->mode == PIPELINE)
476-
#define IF_NOT_PIPELINE() if (redis_sock->mode != PIPELINE)
478+
#define IF_MULTI() if (redis_sock->mode & MULTI)
479+
#define IF_NOT_MULTI() if (!(redis_sock->mode & MULTI))
480+
#define IF_PIPELINE() if (redis_sock->mode & PIPELINE)
481+
#define IF_NOT_PIPELINE() if (!(redis_sock->mode & PIPELINE))
477482

478483
#define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \
479484
if (redis_sock->pipeline_cmd == NULL) { \
@@ -494,14 +499,14 @@ typedef enum _PUBSUB_TYPE {
494499
}
495500

496501
#define REDIS_SAVE_CALLBACK(callback, closure_context) do { \
497-
fold_item *f1 = malloc(sizeof(fold_item)); \
498-
f1->fun = (void *)callback; \
499-
f1->ctx = closure_context; \
500-
f1->next = NULL; \
502+
fold_item *fi = malloc(sizeof(fold_item)); \
503+
fi->fun = (void *)callback; \
504+
fi->ctx = closure_context; \
505+
fi->next = NULL; \
501506
if (redis_sock->current) { \
502-
redis_sock->current->next = f1; \
507+
redis_sock->current->next = fi; \
503508
} \
504-
redis_sock->current = f1; \
509+
redis_sock->current = fi; \
505510
if (NULL == redis_sock->head) { \
506511
redis_sock->head = redis_sock->current; \
507512
} \
@@ -516,7 +521,7 @@ typedef enum _PUBSUB_TYPE {
516521
efree(cmd);
517522

518523
#define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \
519-
IF_MULTI() { \
524+
IF_NOT_PIPELINE() { \
520525
if (redis_response_enqueued(redis_sock TSRMLS_CC) != SUCCESS) { \
521526
RETURN_FALSE; \
522527
} \
@@ -592,7 +597,8 @@ typedef enum _PUBSUB_TYPE {
592597
#define IS_LEX_ARG(s,l) \
593598
(l>0 && (*s=='(' || *s=='[' || (l==1 && (*s=='+' || *s=='-'))))
594599

595-
typedef enum {ATOMIC, MULTI, PIPELINE} redis_mode;
600+
#define REDIS_ENABLE_MODE(redis_sock, m) (redis_sock->mode |= m)
601+
#define REDIS_DISABLE_MODE(redis_sock, m) (redis_sock->mode &= ~m)
596602

597603
typedef struct fold_item {
598604
zval * (*fun)(INTERNAL_FUNCTION_PARAMETERS, void *, ...);
@@ -621,7 +627,7 @@ typedef struct {
621627
char *prefix;
622628
int prefix_len;
623629

624-
redis_mode mode;
630+
short mode;
625631
fold_item *head;
626632
fold_item *current;
627633

redis.c

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,9 +1242,11 @@ PHP_METHOD(Redis,__destruct) {
12421242

12431243
// If we think we're in MULTI mode, send a discard
12441244
IF_MULTI() {
1245-
// Discard any multi commands, and free any callbacks that have been
1246-
// queued
1247-
redis_send_discard(redis_sock TSRMLS_CC);
1245+
IF_NOT_PIPELINE() {
1246+
// Discard any multi commands, and free any callbacks that have been
1247+
// queued
1248+
redis_send_discard(redis_sock TSRMLS_CC);
1249+
}
12481250
free_reply_callbacks(redis_sock);
12491251
}
12501252
}
@@ -2617,27 +2619,30 @@ PHP_METHOD(Redis, multi)
26172619
RETURN_FALSE;
26182620
} else {
26192621
free_reply_callbacks(redis_sock);
2620-
redis_sock->mode = PIPELINE;
2622+
REDIS_ENABLE_MODE(redis_sock, PIPELINE);
26212623
}
26222624
} else if (multi_value == MULTI) {
26232625
IF_MULTI() {
26242626
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Already in multi mode");
2625-
} else IF_PIPELINE() {
2626-
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't activate multi in pipeline mode!");
2627-
RETURN_FALSE;
26282627
} else {
26292628
cmd_len = REDIS_SPPRINTF(&cmd, "MULTI", "");
2630-
SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len)
2631-
efree(cmd);
2632-
2633-
if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) {
2634-
RETURN_FALSE;
2635-
} else if (strncmp(resp, "+OK", 3) != 0) {
2629+
IF_PIPELINE() {
2630+
PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len);
2631+
efree(cmd);
2632+
REDIS_SAVE_CALLBACK(NULL, NULL);
2633+
REDIS_ENABLE_MODE(redis_sock, MULTI);
2634+
} else {
2635+
SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len)
2636+
efree(cmd);
2637+
if ((resp = redis_sock_read(redis_sock, &resp_len TSRMLS_CC)) == NULL) {
2638+
RETURN_FALSE;
2639+
} else if (strncmp(resp, "+OK", 3) != 0) {
2640+
efree(resp);
2641+
RETURN_FALSE;
2642+
}
26362643
efree(resp);
2637-
RETURN_FALSE;
2644+
REDIS_ENABLE_MODE(redis_sock, MULTI);
26382645
}
2639-
efree(resp);
2640-
redis_sock->mode = MULTI;
26412646
}
26422647
} else {
26432648
RETURN_FALSE;
@@ -2711,13 +2716,20 @@ PHP_METHOD(Redis, exec)
27112716

27122717
IF_MULTI() {
27132718
cmd_len = REDIS_SPPRINTF(&cmd, "EXEC", "");
2719+
IF_PIPELINE() {
2720+
PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len);
2721+
efree(cmd);
2722+
REDIS_SAVE_CALLBACK(NULL, NULL);
2723+
REDIS_DISABLE_MODE(redis_sock, MULTI);
2724+
RETURN_ZVAL(getThis(), 1, 0);
2725+
}
27142726
SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len)
27152727
efree(cmd);
27162728

27172729
ret = redis_sock_read_multibulk_multi_reply(
27182730
INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock);
27192731
free_reply_callbacks(redis_sock);
2720-
redis_sock->mode = ATOMIC;
2732+
REDIS_DISABLE_MODE(redis_sock, MULTI);
27212733
redis_sock->watching = 0;
27222734
if (ret < 0) {
27232735
zval_dtor(return_value);
@@ -2743,7 +2755,7 @@ PHP_METHOD(Redis, exec)
27432755
redis_sock->pipeline_len = 0;
27442756
}
27452757
free_reply_callbacks(redis_sock);
2746-
redis_sock->mode = ATOMIC;
2758+
REDIS_DISABLE_MODE(redis_sock, PIPELINE);
27472759
}
27482760
}
27492761

@@ -2770,8 +2782,35 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS,
27702782
fold_item *fi;
27712783

27722784
for (fi = redis_sock->head; fi; fi = fi->next) {
2773-
fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab,
2774-
fi->ctx TSRMLS_CC);
2785+
if (fi->fun) {
2786+
fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab,
2787+
fi->ctx TSRMLS_CC);
2788+
continue;
2789+
}
2790+
size_t len;
2791+
char inbuf[255];
2792+
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) {
2793+
} else if (strncmp(inbuf, "+OK", 3) != 0) {
2794+
}
2795+
while ((fi = fi->next) && fi->fun) {
2796+
if (redis_response_enqueued(redis_sock TSRMLS_CC) == SUCCESS) {
2797+
} else {
2798+
}
2799+
}
2800+
if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len TSRMLS_CC) < 0) {
2801+
}
2802+
#if (PHP_MAJOR_VERSION < 7)
2803+
zval *z_ret;
2804+
MAKE_STD_ZVAL(z_ret);
2805+
#else
2806+
zval zv, *z_ret = &zv;
2807+
#endif
2808+
array_init(z_ret);
2809+
add_next_index_zval(z_tab, z_ret);
2810+
2811+
int num = atol(inbuf + 1);
2812+
if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, z_ret TSRMLS_CC) < 0) {
2813+
}
27752814
}
27762815
redis_sock->current = fi;
27772816
return 0;
@@ -2789,19 +2828,20 @@ PHP_METHOD(Redis, pipeline)
27892828
RETURN_FALSE;
27902829
}
27912830

2792-
IF_MULTI() {
2793-
php_error_docref(NULL TSRMLS_CC, E_ERROR,
2794-
"Can't activate pipeline in multi mode!");
2795-
RETURN_FALSE;
2796-
} else IF_PIPELINE() {
2797-
php_error_docref(NULL TSRMLS_CC, E_WARNING,
2831+
IF_PIPELINE() {
2832+
php_error_docref(NULL TSRMLS_CC, E_WARNING,
27982833
"Already in pipeline mode");
27992834
} else {
2835+
IF_MULTI() {
2836+
php_error_docref(NULL TSRMLS_CC, E_ERROR,
2837+
"Can't activate pipeline in multi mode!");
2838+
RETURN_FALSE;
2839+
}
28002840
/* NB : we keep the function fold, to detect the last function.
28012841
* We need the response format of the n - 1 command. So, we can delete
28022842
* when n > 2, the { 1 .. n - 2} commands */
28032843
free_reply_callbacks(redis_sock);
2804-
redis_sock->mode = PIPELINE;
2844+
REDIS_ENABLE_MODE(redis_sock, PIPELINE);
28052845
}
28062846
RETURN_ZVAL(getThis(), 1, 0);
28072847
}
@@ -3521,7 +3561,13 @@ PHP_METHOD(Redis, getMode) {
35213561
RETURN_FALSE;
35223562
}
35233563

3524-
RETVAL_LONG(redis_sock->mode);
3564+
IF_PIPELINE() {
3565+
RETVAL_LONG(PIPELINE);
3566+
} else IF_MULTI() {
3567+
RETVAL_LONG(MULTI);
3568+
} else {
3569+
RETVAL_LONG(ATOMIC);
3570+
}
35253571
}
35263572

35273573
/* {{{ proto Redis::time() */

tests/RedisTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,27 @@ public function testPipeline() {
25332533
$this->redis->setOption(Redis::OPT_PREFIX, "");
25342534
}
25352535

2536+
public function testPipelineMultiExec()
2537+
{
2538+
if (!$this->havePipeline()) {
2539+
$this->markTestSkipped();
2540+
}
2541+
2542+
$ret = $this->redis->pipeline()->multi()->exec()->exec();
2543+
$this->assertTrue(is_array($ret));
2544+
$this->assertEquals(1, count($ret)); // empty transaction
2545+
2546+
$ret = $this->redis->pipeline()
2547+
->ping()
2548+
->multi()->set('x', 42)->incr('x')->exec()
2549+
->ping()
2550+
->multi()->get('x')->del('x')->exec()
2551+
->ping()
2552+
->exec();
2553+
$this->assertTrue(is_array($ret));
2554+
$this->assertEquals(5, count($ret)); // should be 5 atomic operations
2555+
}
2556+
25362557
protected function sequence($mode) {
25372558
$ret = $this->redis->multi($mode)
25382559
->set('x', 42)

0 commit comments

Comments
 (0)