From 6c4923666ecdb08da5e239fd2fcbc892e89af4ff Mon Sep 17 00:00:00 2001 From: Heinonen Arvo Date: Wed, 15 May 2019 15:55:44 +0300 Subject: [PATCH] Add new redis command DELMPUB 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 Change-Id: I9ee19675589ef1d0c19b2525281dfc2ac769da99 --- redismodule/README.md | 6 + redismodule/src/exstrings.c | 33 ++++ redismodule/tst/mock/include/exstringsStub.h | 1 + redismodule/tst/src/exstrings_test.cpp | 269 +++++++++++++++++++++++++++ 4 files changed, 309 insertions(+) diff --git a/redismodule/README.md b/redismodule/README.md index a49c3da..7add0a6 100755 --- a/redismodule/README.md +++ b/redismodule/README.md @@ -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) diff --git a/redismodule/src/exstrings.c b/redismodule/src/exstrings.c index 6570889..cec45ab 100755 --- a/redismodule/src/exstrings.c +++ b/redismodule/src/exstrings.c @@ -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; diff --git a/redismodule/tst/mock/include/exstringsStub.h b/redismodule/tst/mock/include/exstringsStub.h index 9886d00..d61a31b 100755 --- a/redismodule/tst/mock/include/exstringsStub.h +++ b/redismodule/tst/mock/include/exstringsStub.h @@ -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); diff --git a/redismodule/tst/src/exstrings_test.cpp b/redismodule/tst/src/exstrings_test.cpp index 020da95..e051e41 100755 --- a/redismodule/tst/src/exstrings_test.cpp +++ b/redismodule/tst/src/exstrings_test.cpp @@ -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; -- 2.16.6