Fix handling of NIL invalidation messages.

When CLIENT TRACKING is enabled, Redis will send an invalidation message
with a NIL payload to all tracking clients after a FLUSHDB is executed.

We didn't account for REDIS_REPLY_PUSH being a valid parent object to a
NIL payload, and were failing an assertion.

Additionally this commit adds a regression test for the logic.
This commit is contained in:
michael-grunder 2020-10-17 18:46:21 -07:00
parent acc917548d
commit b9b9f446fe
2 changed files with 33 additions and 8 deletions

View File

@ -257,7 +257,8 @@ static void *createNilObject(const redisReadTask *task) {
parent = task->parent->obj; parent = task->parent->obj;
assert(parent->type == REDIS_REPLY_ARRAY || assert(parent->type == REDIS_REPLY_ARRAY ||
parent->type == REDIS_REPLY_MAP || parent->type == REDIS_REPLY_MAP ||
parent->type == REDIS_REPLY_SET); parent->type == REDIS_REPLY_SET ||
parent->type == REDIS_REPLY_PUSH);
parent->element[task->idx] = r; parent->element[task->idx] = r;
} }
return r; return r;

38
test.c
View File

@ -53,6 +53,11 @@ struct privdata {
int dtor_counter; int dtor_counter;
}; };
struct pushCounters {
int nil;
int str;
};
#ifdef HIREDIS_TEST_SSL #ifdef HIREDIS_TEST_SSL
redisSSLContext *_ssl_ctx = NULL; redisSSLContext *_ssl_ctx = NULL;
#endif #endif
@ -677,11 +682,25 @@ static void test_blocking_connection_errors(void) {
#endif #endif
} }
/* Dummy push handler */ /* Test push handler */
void push_handler(void *privdata, void *reply) { void push_handler(void *privdata, void *r) {
int *counter = privdata; struct pushCounters *pcounts = privdata;
redisReply *reply = r, *payload;
assert(reply && reply->type == REDIS_REPLY_PUSH && reply->elements == 2);
payload = reply->element[1];
if (payload->type == REDIS_REPLY_ARRAY) {
payload = payload->element[0];
}
if (payload->type == REDIS_REPLY_STRING) {
pcounts->str++;
} else if (payload->type == REDIS_REPLY_NIL) {
pcounts->nil++;
}
freeReplyObject(reply); freeReplyObject(reply);
*counter += 1;
} }
/* Dummy function just to test setting a callback with redisOptions */ /* Dummy function just to test setting a callback with redisOptions */
@ -691,16 +710,16 @@ void push_handler_async(redisAsyncContext *ac, void *reply) {
} }
static void test_resp3_push_handler(redisContext *c) { static void test_resp3_push_handler(redisContext *c) {
struct pushCounters pc = {0};
redisPushFn *old = NULL; redisPushFn *old = NULL;
redisReply *reply; redisReply *reply;
void *privdata; void *privdata;
int n = 0;
/* Switch to RESP3 and turn on client tracking */ /* Switch to RESP3 and turn on client tracking */
send_hello(c, 3); send_hello(c, 3);
send_client_tracking(c, "ON"); send_client_tracking(c, "ON");
privdata = c->privdata; privdata = c->privdata;
c->privdata = &n; c->privdata = &pc;
reply = redisCommand(c, "GET key:0"); reply = redisCommand(c, "GET key:0");
assert(reply != NULL); assert(reply != NULL);
@ -717,7 +736,12 @@ static void test_resp3_push_handler(redisContext *c) {
old = redisSetPushCallback(c, push_handler); old = redisSetPushCallback(c, push_handler);
test("We can set a custom RESP3 PUSH handler: "); test("We can set a custom RESP3 PUSH handler: ");
reply = redisCommand(c, "SET key:0 val:0"); reply = redisCommand(c, "SET key:0 val:0");
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && n == 1); test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && pc.str == 1);
freeReplyObject(reply);
test("We properly handle a NIL invalidation payload: ");
reply = redisCommand(c, "FLUSHDB");
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && pc.nil == 1);
freeReplyObject(reply); freeReplyObject(reply);
/* Unset the push callback and generate an invalidate message making /* Unset the push callback and generate an invalidate message making