From 00a8e72cfc9075e647b447a8cc4d4709302156f0 Mon Sep 17 00:00:00 2001 From: "Filipe Oliveira (Redis)" Date: Tue, 3 Sep 2024 11:32:43 +0100 Subject: [PATCH] Created specific SMEMBERS command logic which avoids sinterGenericCommand, and minimizes processing and memory overhead (#13499) This PR introduces a dedicated implementation for the SMEMBERS command that avoids using the more generalized sinterGenericCommand function. By tailoring the logic specifically for SMEMBERS, we reduce unnecessary processing and memory overheads that were previously incurred by handling more complex cases like set intersections. --------- Co-authored-by: debing.sun --- src/commands.def | 2 +- src/commands/smembers.json | 2 +- src/server.h | 1 + src/t_set.c | 29 ++++++++++++++++++++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/commands.def b/src/commands.def index f8ccdf0713..ef42fb8da7 100644 --- a/src/commands.def +++ b/src/commands.def @@ -11145,7 +11145,7 @@ struct COMMAND_STRUCT redisCommandTable[] = { {MAKE_CMD("sintercard","Returns the number of members of the intersect of multiple sets.","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","7.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SINTERCARD_History,0,SINTERCARD_Tips,0,sinterCardCommand,-3,CMD_READONLY,ACL_CATEGORY_SET,SINTERCARD_Keyspecs,1,sintercardGetKeys,3),.args=SINTERCARD_Args}, {MAKE_CMD("sinterstore","Stores the intersect of multiple sets in a key.","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SINTERSTORE_History,0,SINTERSTORE_Tips,0,sinterstoreCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET,SINTERSTORE_Keyspecs,2,NULL,2),.args=SINTERSTORE_Args}, {MAKE_CMD("sismember","Determines whether a member belongs to a set.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SISMEMBER_History,0,SISMEMBER_Tips,0,sismemberCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,SISMEMBER_Keyspecs,1,NULL,2),.args=SISMEMBER_Args}, -{MAKE_CMD("smembers","Returns all members of a set.","O(N) where N is the set cardinality.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMEMBERS_History,0,SMEMBERS_Tips,1,sinterCommand,2,CMD_READONLY,ACL_CATEGORY_SET,SMEMBERS_Keyspecs,1,NULL,1),.args=SMEMBERS_Args}, +{MAKE_CMD("smembers","Returns all members of a set.","O(N) where N is the set cardinality.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMEMBERS_History,0,SMEMBERS_Tips,1,smembersCommand,2,CMD_READONLY,ACL_CATEGORY_SET,SMEMBERS_Keyspecs,1,NULL,1),.args=SMEMBERS_Args}, {MAKE_CMD("smismember","Determines whether multiple members belong to a set.","O(N) where N is the number of elements being checked for membership","6.2.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMISMEMBER_History,0,SMISMEMBER_Tips,0,smismemberCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,SMISMEMBER_Keyspecs,1,NULL,2),.args=SMISMEMBER_Args}, {MAKE_CMD("smove","Moves a member from one set to another.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMOVE_History,0,SMOVE_Tips,0,smoveCommand,4,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,SMOVE_Keyspecs,2,NULL,3),.args=SMOVE_Args}, {MAKE_CMD("spop","Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.","Without the count argument O(1), otherwise O(N) where N is the value of the passed count.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SPOP_History,1,SPOP_Tips,1,spopCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,SPOP_Keyspecs,1,NULL,2),.args=SPOP_Args}, diff --git a/src/commands/smembers.json b/src/commands/smembers.json index c5114089b9..8878c18c12 100644 --- a/src/commands/smembers.json +++ b/src/commands/smembers.json @@ -5,7 +5,7 @@ "group": "set", "since": "1.0.0", "arity": 2, - "function": "sinterCommand", + "function": "smembersCommand", "command_flags": [ "READONLY" ], diff --git a/src/server.h b/src/server.h index 79cbafde5a..fe2da0a4a2 100644 --- a/src/server.h +++ b/src/server.h @@ -3639,6 +3639,7 @@ void scardCommand(client *c); void spopCommand(client *c); void srandmemberCommand(client *c); void sinterCommand(client *c); +void smembersCommand(client *c); void sinterCardCommand(client *c); void sinterstoreCommand(client *c); void sunionCommand(client *c); diff --git a/src/t_set.c b/src/t_set.c index f16cde8183..1aefda0d59 100644 --- a/src/t_set.c +++ b/src/t_set.c @@ -1244,7 +1244,7 @@ int qsortCompareSetsByRevCardinality(const void *s1, const void *s2) { return 0; } -/* SINTER / SMEMBERS / SINTERSTORE / SINTERCARD +/* SINTER / SINTERSTORE / SINTERCARD * * 'cardinality_only' work for SINTERCARD, only return the cardinality * with minimum processing and memory overheads. @@ -1420,6 +1420,33 @@ void sinterCommand(client *c) { sinterGenericCommand(c, c->argv+1, c->argc-1, NULL, 0, 0); } +/* SMEMBERS key */ +void smembersCommand(client *c) { + setTypeIterator *si; + char *str; + size_t len; + int64_t intobj; + robj *setobj = lookupKeyRead(c->db, c->argv[1]); + if (checkType(c,setobj,OBJ_SET)) return; + if (!setobj) { + addReply(c, shared.emptyset[c->resp]); + return; + } + + /* Prepare the response. */ + addReplySetLen(c,setTypeSize(setobj)); + + /* Iterate through the elements of the set. */ + si = setTypeInitIterator(setobj); + while (setTypeNext(si, &str, &len, &intobj) != -1) { + if (str != NULL) + addReplyBulkCBuffer(c, str, len); + else + addReplyBulkLongLong(c, intobj); + } + setTypeReleaseIterator(si); +} + /* SINTERCARD numkeys key [key ...] [LIMIT limit] */ void sinterCardCommand(client *c) { long j;