Add new redis command MSETMPUB 24/224/4
authorHeinonen Arvo <arvo.heinonen@nokia.com>
Tue, 14 May 2019 08:23:44 +0000 (11:23 +0300)
committerArvo Heinonen <arvo.heinonen@nokia.com>
Tue, 4 Jun 2019 13:22:06 +0000 (16:22 +0300)
Add new redis command MSETMPUB which is functionally identical to
MSETPUB except that it allows to specify multiple channel-message pairs
at once.

The syntax for the new command is:
MSETMPUB number_of_key_value_pairs number_of_channel_message_pairs
         key value [ key value ... ]
 channel message [ channel message ... ]

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

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

index b60a8a5..b6133ed 100755 (executable)
@@ -123,6 +123,12 @@ Time complexity: O(N) where N is the number of keys to set + O(N+M) where N is t
 
 Set the given keys to their respective values and post a message to the given channel
 
+## MSETMPUB number_of_key_value_pairs number_of_channel_message_pairs key value [ key value ... ] channel message [ channel message ... ]
+
+Time complexity: O(N) where N is the number of keys to set + O(N_1+M) [ + O(N_2+M) + ... ] where N_i are the number of clients subscribed to the corresponding receiving channel and M is the total number of subscribed patterns (by any client)
+
+Set the given keys to their respective values and post messages to their respective channels
+
 ## SETXXPUB key value channel message [channel message...]
 
 Time complexity: O(1) + O(1) + 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).
index 436ccae..ef3cf5d 100755 (executable)
@@ -356,6 +356,35 @@ int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
     return setPubStringCommon(ctx, &setParams, &pubParams);
 }
 
+int SetMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    if (argc < 7 || (argc % 2) == 0)
+        return RedisModule_WrongArity(ctx);
+
+    long long setPairsCount, pubPairsCount;
+    RedisModule_StringToLongLong(argv[1], &setPairsCount);
+    RedisModule_StringToLongLong(argv[2], &pubPairsCount);
+    if (setPairsCount < 1 || pubPairsCount < 1)
+        return RedisModule_ReplyWithError(ctx, "ERR SET_PAIR_COUNT and PUB_PAIR_COUNT must be greater than zero");
+
+    long long setLen, pubLen;
+    setLen = 2*setPairsCount;
+    pubLen = 2*pubPairsCount;
+    if (setLen + pubLen + 3 != argc)
+        return RedisModule_ReplyWithError(ctx, "ERR SET_PAIR_COUNT or PUB_PAIR_COUNT do not match the total pair count");
+
+    SetParams setParams = {
+                           .key_val_pairs = argv + 3,
+                           .length = setLen
+                          };
+    PubParams pubParams = {
+                           .channel_msg_pairs = argv + 3 + setParams.length,
+                           .length = pubLen
+                          };
+
+    return setPubStringCommon(ctx, &setParams, &pubParams);
+}
+
 int setIENEPubStringCommon(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int flag)
 {
     if (argc < 6 || (argc % 2) != 0)
@@ -577,6 +606,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
         SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
         return REDISMODULE_ERR;
 
+    if (RedisModule_CreateCommand(ctx,"msetmpub",
+        SetMPub_RedisCommand,"write deny-oom pubsub",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
     if (RedisModule_CreateCommand(ctx,"setiepub",
         SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
         return REDISMODULE_ERR;
index 4ce6a0f..23842cf 100755 (executable)
@@ -33,6 +33,7 @@ int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int a
 int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int SetMPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
 int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
index df79c72..1e97588 100755 (executable)
@@ -361,6 +361,7 @@ int RedisModule_ReplyWithNull(RedisModuleCtx *ctx);
 int RedisModule_ReplyWithCallReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply);
 const char *RedisModule_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len);
 RedisModuleString *RedisModule_CreateStringFromCallReply(RedisModuleCallReply *reply);
+int RedisModule_StringToLongLong(const RedisModuleString *str, long long *ll);
 
 int RedisModule_KeyType(RedisModuleKey *kp);
 void RedisModule_CloseKey(RedisModuleKey *kp);
index b08fd21..2e401b7 100755 (executable)
@@ -95,7 +95,7 @@ RedisModuleCallReply *RedisModule_Call(RedisModuleCtx *ctx, const char *cmdname,
     else if (!strcmp(cmdname, "UNLINK"))
         mock().setData("UNLINK", 1);
     else if (!strcmp(cmdname, "PUBLISH"))
-        mock().setData("PUBLISH", 1);
+        mock().setData("PUBLISH", mock().getData("PUBLISH").getIntValue() + 1);
     else if (!strcmp(cmdname, "KEYS"))
         mock().setData("KEYS", 1);
     else if (!strcmp(cmdname, "MGET"))
@@ -289,3 +289,20 @@ int RedisModule_ReplyWithArray(RedisModuleCtx *ctx, long len)
     return REDISMODULE_OK;
 }
 
+int RedisModule_StringToLongLong(const RedisModuleString *str, long long *ll)
+{
+    (void) str;
+    int call_no = mock().getData("RedisModule_StringToLongLongCallCount").getIntValue();
+    switch(call_no) {
+        case 0:
+            *ll = mock().getData("RedisModule_StringToLongLongCall_1").getIntValue();
+            break;
+        case 1:
+            *ll = mock().getData("RedisModule_StringToLongLongCall_2").getIntValue();
+            break;
+        default:
+            *ll = mock().getData("RedisModule_StringToLongLongCallDefault").getIntValue();
+    }
+    mock().setData("RedisModule_StringToLongLongCallCount", call_no + 1);
+    return REDISMODULE_OK;
+}
index 7a22d79..a131781 100755 (executable)
@@ -463,6 +463,28 @@ TEST(exstring, setpub)
     delete []redisStrVec;
 }
 
+TEST(exstring, setmpub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[11]);
+
+    for (int i = 0 ; i < 11 ; ++i)
+        redisStrVec[i] = (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", 2);
+    mock().setData("RedisModule_StringToLongLongCall_2", 2);
+
+    int ret = SetMPub_RedisCommand(&ctx, redisStrVec, 11);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
 TEST(exstring, setxxpub)
 {
     RedisModuleCtx ctx;
@@ -569,6 +591,14 @@ TEST(exstring, setpub_command_parameter_number_incorrect)
     ret = SetPub_RedisCommand(&ctx, 0, 8);
     CHECK_EQUAL(ret, REDISMODULE_ERR);
 
+    ret = 0;
+    ret = SetMPub_RedisCommand(&ctx, 0, 2);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    ret = 0;
+    ret = SetMPub_RedisCommand(&ctx, 0, 8);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
     ret = 0;
     ret = SetXXPub_RedisCommand(&ctx, 0, 3);
     CHECK_EQUAL(ret, REDISMODULE_ERR);
@@ -654,6 +684,193 @@ TEST(exstring, setpub_command_no_key_replystr)
 
 }
 
+TEST(exstring, setmpub_command_no_key_replynull)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[9]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+    redisStrVec[6] = (RedisModuleString *)6;
+    redisStrVec[7] = (RedisModuleString *)7;
+    redisStrVec[8] = (RedisModuleString *)8;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 2);
+
+    int ret = SetMPub_RedisCommand(&ctx, redisStrVec, 9);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(1, mock().getData("MSET").getIntValue());
+    CHECK_EQUAL(0, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(1, mock().getData("RedisModule_FreeCallReply").getIntValue());
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setmpub_command_negative_key_val_count)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[7]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+    redisStrVec[6] = (RedisModuleString *)6;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_StringToLongLongCall_1", -1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 1);
+
+    int ret = SetMPub_RedisCommand(&ctx, redisStrVec, 7);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(0, mock().getData("MSET").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, setmpub_command_negative_chan_msg_count)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[7]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+    redisStrVec[6] = (RedisModuleString *)6;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", -1);
+
+    int ret = SetMPub_RedisCommand(&ctx, redisStrVec, 7);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(0, mock().getData("MSET").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, setmpub_command_invalid_total_count)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[7]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+    redisStrVec[6] = (RedisModuleString *)6;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_StringToLongLongCall_1", 100);
+    mock().setData("RedisModule_StringToLongLongCall_2", 100);
+
+    int ret = SetMPub_RedisCommand(&ctx, redisStrVec, 7);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(0, mock().getData("MSET").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, setmpub_command_set)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[7]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+    redisStrVec[6] = (RedisModuleString *)6;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 1);
+
+    int ret = SetMPub_RedisCommand(&ctx, redisStrVec, 7);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(1, mock().getData("MSET").getIntValue());
+    CHECK_EQUAL(1, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(2, mock().getData("RedisModule_FreeCallReply").getIntValue());
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setmpub_command_set_multipub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[9]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+    redisStrVec[6] = (RedisModuleString *)6;
+    redisStrVec[7] = (RedisModuleString *)7;
+    redisStrVec[8] = (RedisModuleString *)8;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_StringToLongLongCall_1", 1);
+    mock().setData("RedisModule_StringToLongLongCall_2", 2);
+
+    int ret = SetMPub_RedisCommand(&ctx, redisStrVec, 9);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(0, mock().getData("GET").getIntValue());
+    CHECK_EQUAL(1, mock().getData("MSET").getIntValue());
+    CHECK_EQUAL(2, mock().getData("PUBLISH").getIntValue());
+    CHECK_EQUAL(3, mock().getData("RedisModule_FreeCallReply").getIntValue());
+
+    delete []redisStrVec;
+
+}
+
 TEST(exstring, setxxpub_command_has_no_key)
 {
     RedisModuleCtx ctx;