Add new redis command DELMPUB 28/228/4
authorHeinonen Arvo <arvo.heinonen@nokia.com>
Wed, 15 May 2019 12:55:44 +0000 (15:55 +0300)
committerArvo Heinonen <arvo.heinonen@nokia.com>
Tue, 4 Jun 2019 13:33:15 +0000 (16:33 +0300)
Add new redis command DELMPUB which is functionally identical to
DELMPUB except that it allows to specify multiple channel-message pairs
at once.

The syntax for the new command is:
DELMPUB number_of_keys number_of_channel_message_pairs
        key [ key ... ]
        channel message [ channel message ... ]

Signed-off-by: Arvo Heinonen <arvo.heinonen@nokia.com>
Change-Id: I9ee19675589ef1d0c19b2525281dfc2ac769da99

redismodule/README.md
redismodule/src/exstrings.c
redismodule/tst/mock/include/exstringsStub.h
redismodule/tst/src/exstrings_test.cpp

index a49c3da..7add0a6 100755 (executable)
@@ -159,6 +159,12 @@ Time complexity: O(N) where N is the number of keys that will be removed + O(N+M
 
 Removes the specified keys and post a message to the given channel if delete key successfully(return >0)
 
+## DELMPUB number_of_keys number_of_channel_message_pairs key [ key ... ] channel message [ channel message ... ]
+
+Time complexity: O(N) where N is the number of keys that will be removed + O(N_1+M) [ + O(N_2+M) + ... ] where N_i are the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Remove the specified keys. If any of the keys was deleted succesfully (delete return value > 0) then post given messages to the corresponding channels.
+
 ## DELIEPUB key oldvalue channel message [channel message...]
 
 Time complexity: O(1) + O(1) + O(1) + O(N_1+M) [ + O(N_2+M) + ...] where N_i are the number of clients subscribed to the corrensponding receiving channel and M is the total number of subscribed patterns (by any client)
index 6570889..cec45ab 100755 (executable)
@@ -506,6 +506,35 @@ int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
     return delPubStringCommon(ctx, &delParams, &pubParams);
 }
 
+int DelMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    if (argc < 6)
+        return RedisModule_WrongArity(ctx);
+
+    long long delCount, pubPairsCount;
+    RedisModule_StringToLongLong(argv[1], &delCount);
+    RedisModule_StringToLongLong(argv[2], &pubPairsCount);
+    if (delCount < 1 || pubPairsCount < 1)
+        return RedisModule_ReplyWithError(ctx, "ERR DEL_COUNT and PUB_PAIR_COUNT must be greater than zero");
+
+    long long delLen, pubLen;
+    delLen = delCount;
+    pubLen = 2*pubPairsCount;
+    if (delLen + pubLen + 3 != argc)
+        return RedisModule_ReplyWithError(ctx, "ERR DEL_COUNT or PUB_PAIR_COUNT do not match the total pair count");
+
+    DelParams delParams = {
+                           .keys = argv + 3,
+                           .length = delLen
+                          };
+    PubParams pubParams = {
+                           .channel_msg_pairs = argv + 3 + delParams.length,
+                           .length = pubLen
+                          };
+
+    return delPubStringCommon(ctx, &delParams, &pubParams);
+}
+
 int delIENEPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
 {
     if (argc < 5 || (argc % 2) == 0)
@@ -612,6 +641,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
         DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
         return REDISMODULE_ERR;
 
+    if (RedisModule_CreateCommand(ctx,"delmpub",
+        DelMPub_RedisCommand,"write deny-oom pubsub",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
     if (RedisModule_CreateCommand(ctx,"deliepub",
         DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
         return REDISMODULE_ERR;
index 9886d00..d61a31b 100755 (executable)
@@ -39,6 +39,7 @@ int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int arg
 int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int DelMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
index 020da95..e051e41 100755 (executable)
@@ -1257,6 +1257,34 @@ TEST(exstring, delpub)
     delete []redisStrVec;
 }
 
+TEST(exstring, delmpub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[8]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+    redisStrVec[6] = (RedisModuleString *)1;
+    redisStrVec[7] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 2);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec,  8);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+
+    delete []redisStrVec;
+}
+
 TEST(exstring, deliepub)
 {
     RedisModuleCtx ctx;
@@ -1310,6 +1338,10 @@ TEST(exstring, delpub_command_parameter_number_incorrect)
     ret = DelPub_RedisCommand(&ctx, 0, 2);
     CHECK_EQUAL(ret, REDISMODULE_ERR);
 
+    ret = 0;
+    ret = DelMPub_RedisCommand(&ctx, 0, 5);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
     ret = 0;
     ret = DelIEPub_RedisCommand(&ctx, 0, 4);
     CHECK_EQUAL(ret, REDISMODULE_ERR);
@@ -1394,6 +1426,243 @@ TEST(exstring, delpub_command_has_no_key)
 
 }
 
+TEST(exstring, delmpub_command_reply_null)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString **redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_Call_Return_Null", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 1);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec, 6);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(1, mock().getData("UNLINK").getIntValue());
+    CHECK_EQUAL(0, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(0, mock().getData("RedisModule_ReplyWithCallReply").getIntValue());
+    CHECK_EQUAL(0, mock().getData("RedisModule_FreeCallReply").getIntValue());
+    CHECK_EQUAL(1, mock().getData("RedisModule_ReplyWithError").getIntValue());
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delmpub_command_reply_error)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString **redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyInteger", 0);
+    mock().setData("RedisModule_CallReplyType_err", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 1);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec, 6);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delmpub_command_has_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString **redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyInteger", 0);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 1);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec, 6);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delmpub_command_key_deleted)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString **redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyInteger", 1);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 1);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec, 6);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(1, mock().getData("UNLINK").getIntValue());
+    CHECK_EQUAL(1, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(1, mock().getData("RedisModule_ReplyWithCallReply").getIntValue());
+    CHECK_EQUAL(2, mock().getData("RedisModule_FreeCallReply").getIntValue());
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delmpub_command_key_deleted_multi_pub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString **redisStrVec = new (RedisModuleString*[10]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+    redisStrVec[6] = (RedisModuleString *)1;
+    redisStrVec[7] = (RedisModuleString *)1;
+    redisStrVec[8] = (RedisModuleString *)1;
+    redisStrVec[9] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyInteger", 1);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 3);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec, 10);
+    CHECK_EQUAL(0, ret);
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(1, mock().getData("UNLINK").getIntValue());
+    CHECK_EQUAL(3, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(1, mock().getData("RedisModule_ReplyWithCallReply").getIntValue());
+    CHECK_EQUAL(4, mock().getData("RedisModule_FreeCallReply").getIntValue());
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delmpub_command_negative_del_count)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString **redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyInteger", 1);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", -1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 1);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec, 6);
+    CHECK_EQUAL(0, ret);
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(0, mock().getData("UNLINK").getIntValue());
+    CHECK_EQUAL(0, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(1, mock().getData("RedisModule_ReplyWithError").getIntValue());
+    CHECK_EQUAL(0, mock().getData("RedisModule_FreeCallReply").getIntValue());
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delmpub_command_negative_chan_msg_count)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString **redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyInteger", 1);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", -1);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec, 6);
+    CHECK_EQUAL(0, ret);
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(0, mock().getData("UNLINK").getIntValue());
+    CHECK_EQUAL(0, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(1, mock().getData("RedisModule_ReplyWithError").getIntValue());
+    CHECK_EQUAL(0, mock().getData("RedisModule_FreeCallReply").getIntValue());
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delmpub_command_invalid_total_count)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString **redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyInteger", 1);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+    mock().setData("RedisModule_StringToLongLongCallCount", 0);
+    mock().setData("RedisModule_StringToLongLongCall_1", 100);
+    mock().setData("RedisModule_StringToLongLongCall_2", 100);
+
+    int ret = DelMPub_RedisCommand(&ctx, redisStrVec, 6);
+    CHECK_EQUAL(0, ret);
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(0, mock().getData("UNLINK").getIntValue());
+    CHECK_EQUAL(0, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(1, mock().getData("RedisModule_ReplyWithError").getIntValue());
+    CHECK_EQUAL(0, mock().getData("RedisModule_FreeCallReply").getIntValue());
+    delete []redisStrVec;
+
+}
+
 TEST(exstring, deliepub_command_has_no_key)
 {
     RedisModuleCtx ctx;