diff --git a/src/cli_commands.h b/src/cli_commands.h index a8174e4303b..0bf6d3447d2 100644 --- a/src/cli_commands.h +++ b/src/cli_commands.h @@ -38,6 +38,9 @@ struct commandDocs { int numargs; cliCommandArg *args; /* An array of the command arguments. */ struct commandDocs *subcommands; + int member_key_index; /* unused in CLI, present for commands.def compatibility */ + int member_key_step; + int member_key_count; char *params; /* A string describing the syntax of the command arguments. */ }; diff --git a/src/commands.def b/src/commands.def index ca87553cd5d..cbf1cdb4a74 100644 --- a/src/commands.def +++ b/src/commands.def @@ -11914,33 +11914,33 @@ struct COMMAND_STRUCT serverCommandTable[] = { {MAKE_CMD("geosearch","Queries a geospatial index for members inside an area of a box, circle, or a polygon.","O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape","6.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOSEARCH_History,2,GEOSEARCH_Tips,0,geosearchCommand,-7,CMD_READONLY,ACL_CATEGORY_GEO|ACL_CATEGORY_READ|ACL_CATEGORY_SLOW,NULL,GEOSEARCH_Keyspecs,1,NULL,8),.args=GEOSEARCH_Args}, {MAKE_CMD("geosearchstore","Queries a geospatial index for members inside an area of a box, a circle, or a polygon, optionally stores the result.","O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape","6.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOSEARCHSTORE_History,2,GEOSEARCHSTORE_Tips,0,geosearchstoreCommand,-8,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO|ACL_CATEGORY_SLOW|ACL_CATEGORY_WRITE,NULL,GEOSEARCHSTORE_Keyspecs,2,NULL,7),.args=GEOSEARCHSTORE_Args}, /* hash */ -{MAKE_CMD("hdel","Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.","O(N) where N is the number of fields to be removed.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HDEL_History,1,HDEL_Tips,0,hdelCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HDEL_Keyspecs,1,NULL,2),.args=HDEL_Args}, -{MAKE_CMD("hexists","Determines whether a field exists in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXISTS_History,0,HEXISTS_Tips,0,hexistsCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HEXISTS_Keyspecs,1,NULL,2),.args=HEXISTS_Args}, +{MAKE_CMD("hdel","Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.","O(N) where N is the number of fields to be removed.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HDEL_History,1,HDEL_Tips,0,hdelCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HDEL_Keyspecs,1,NULL,2),.args=HDEL_Args,.member_key_index=2}, +{MAKE_CMD("hexists","Determines whether a field exists in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXISTS_History,0,HEXISTS_Tips,0,hexistsCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HEXISTS_Keyspecs,1,NULL,2),.args=HEXISTS_Args,.member_key_index=2}, {MAKE_CMD("hexpire","Sets expiry time on hash fields.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXPIRE_History,0,HEXPIRE_Tips,0,hexpireCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HEXPIRE_Keyspecs,1,NULL,4),.args=HEXPIRE_Args}, {MAKE_CMD("hexpireat","Sets expiry time on hash fields.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXPIREAT_History,0,HEXPIREAT_Tips,0,hexpireatCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HEXPIREAT_Keyspecs,1,NULL,4),.args=HEXPIREAT_Args}, -{MAKE_CMD("hexpiretime","Returns Unix timestamps in seconds since the epoch at which the given key's field(s) will expire.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXPIRETIME_History,0,HEXPIRETIME_Tips,0,hexpiretimeCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HEXPIRETIME_Keyspecs,1,NULL,2),.args=HEXPIRETIME_Args}, -{MAKE_CMD("hget","Returns the value of a field in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGET_History,0,HGET_Tips,0,hgetCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HGET_Keyspecs,1,NULL,2),.args=HGET_Args}, +{MAKE_CMD("hexpiretime","Returns Unix timestamps in seconds since the epoch at which the given key's field(s) will expire.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXPIRETIME_History,0,HEXPIRETIME_Tips,0,hexpiretimeCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HEXPIRETIME_Keyspecs,1,NULL,2),.args=HEXPIRETIME_Args,.member_key_index=4}, +{MAKE_CMD("hget","Returns the value of a field in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGET_History,0,HGET_Tips,0,hgetCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HGET_Keyspecs,1,NULL,2),.args=HGET_Args,.member_key_index=2}, {MAKE_CMD("hgetall","Returns all fields and values in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETALL_History,0,HGETALL_Tips,1,hgetallCommand,2,CMD_READONLY,ACL_CATEGORY_HASH|ACL_CATEGORY_READ|ACL_CATEGORY_SLOW,NULL,HGETALL_Keyspecs,1,NULL,1),.args=HGETALL_Args}, -{MAKE_CMD("hgetdel","Returns the values of one or more fields and deletes them from a hash.","O(N) where N is the number of fields to be retrieved and deleted.","9.1.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETDEL_History,0,HGETDEL_Tips,0,hgetdelCommand,-5,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HGETDEL_Keyspecs,1,NULL,2),.args=HGETDEL_Args}, +{MAKE_CMD("hgetdel","Returns the values of one or more fields and deletes them from a hash.","O(N) where N is the number of fields to be retrieved and deleted.","9.1.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETDEL_History,0,HGETDEL_Tips,0,hgetdelCommand,-5,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HGETDEL_Keyspecs,1,NULL,2),.args=HGETDEL_Args,.member_key_index=4}, {MAKE_CMD("hgetex","Gets the value of one or more fields of a given hash key, and optionally sets their expiration time or time-to-live (TTL).","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETEX_History,0,HGETEX_Tips,0,hgetexCommand,-5,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HGETEX_Keyspecs,1,NULL,3),.args=HGETEX_Args}, -{MAKE_CMD("hincrby","Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBY_History,0,HINCRBY_Tips,0,hincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HINCRBY_Keyspecs,1,NULL,3),.args=HINCRBY_Args}, -{MAKE_CMD("hincrbyfloat","Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBYFLOAT_History,0,HINCRBYFLOAT_Tips,0,hincrbyfloatCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HINCRBYFLOAT_Keyspecs,1,NULL,3),.args=HINCRBYFLOAT_Args}, +{MAKE_CMD("hincrby","Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBY_History,0,HINCRBY_Tips,0,hincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HINCRBY_Keyspecs,1,NULL,3),.args=HINCRBY_Args,.member_key_index=2,.member_key_count=1}, +{MAKE_CMD("hincrbyfloat","Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBYFLOAT_History,0,HINCRBYFLOAT_Tips,0,hincrbyfloatCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HINCRBYFLOAT_Keyspecs,1,NULL,3),.args=HINCRBYFLOAT_Args,.member_key_index=2,.member_key_count=1}, {MAKE_CMD("hkeys","Returns all fields in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HKEYS_History,0,HKEYS_Tips,1,hkeysCommand,2,CMD_READONLY,ACL_CATEGORY_HASH|ACL_CATEGORY_READ|ACL_CATEGORY_SLOW,NULL,HKEYS_Keyspecs,1,NULL,1),.args=HKEYS_Args}, {MAKE_CMD("hlen","Returns the number of fields in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HLEN_History,0,HLEN_Tips,0,hlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HLEN_Keyspecs,1,NULL,1),.args=HLEN_Args}, -{MAKE_CMD("hmget","Returns the values of all fields in a hash.","O(N) where N is the number of fields being requested.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HMGET_History,0,HMGET_Tips,0,hmgetCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HMGET_Keyspecs,1,NULL,2),.args=HMGET_Args}, -{MAKE_CMD("hmset","Sets the values of multiple fields.","O(N) where N is the number of fields being set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HMSET_History,0,HMSET_Tips,0,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HMSET_Keyspecs,1,NULL,2),.args=HMSET_Args}, -{MAKE_CMD("hpersist","Remove the existing expiration on a hash key's field(s).","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPERSIST_History,0,HPERSIST_Tips,0,hpersistCommand,-5,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HPERSIST_Keyspecs,1,NULL,2),.args=HPERSIST_Args}, +{MAKE_CMD("hmget","Returns the values of all fields in a hash.","O(N) where N is the number of fields being requested.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HMGET_History,0,HMGET_Tips,0,hmgetCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HMGET_Keyspecs,1,NULL,2),.args=HMGET_Args,.member_key_index=2}, +{MAKE_CMD("hmset","Sets the values of multiple fields.","O(N) where N is the number of fields being set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HMSET_History,0,HMSET_Tips,0,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HMSET_Keyspecs,1,NULL,2),.args=HMSET_Args,.member_key_index=2,.member_key_step=2}, +{MAKE_CMD("hpersist","Remove the existing expiration on a hash key's field(s).","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPERSIST_History,0,HPERSIST_Tips,0,hpersistCommand,-5,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HPERSIST_Keyspecs,1,NULL,2),.args=HPERSIST_Args,.member_key_index=4}, {MAKE_CMD("hpexpire","Sets expiry time on hash object.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPEXPIRE_History,0,HPEXPIRE_Tips,0,hpexpireCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HPEXPIRE_Keyspecs,1,NULL,4),.args=HPEXPIRE_Args}, {MAKE_CMD("hpexpireat","Sets expiration time on hash field.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPEXPIREAT_History,0,HPEXPIREAT_Tips,0,hpexpireatCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HPEXPIREAT_Keyspecs,1,NULL,4),.args=HPEXPIREAT_Args}, -{MAKE_CMD("hpexpiretime","Returns the Unix timestamp in milliseconds since Unix epoch at which the given key's field(s) will expire.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPEXPIRETIME_History,0,HPEXPIRETIME_Tips,0,hpexpiretimeCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HPEXPIRETIME_Keyspecs,1,NULL,2),.args=HPEXPIRETIME_Args}, -{MAKE_CMD("hpttl","Returns the remaining time to live in milliseconds of a hash key's field(s) that have an associated expiration.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPTTL_History,0,HPTTL_Tips,0,hpttlCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HPTTL_Keyspecs,1,NULL,2),.args=HPTTL_Args}, +{MAKE_CMD("hpexpiretime","Returns the Unix timestamp in milliseconds since Unix epoch at which the given key's field(s) will expire.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPEXPIRETIME_History,0,HPEXPIRETIME_Tips,0,hpexpiretimeCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HPEXPIRETIME_Keyspecs,1,NULL,2),.args=HPEXPIRETIME_Args,.member_key_index=4}, +{MAKE_CMD("hpttl","Returns the remaining time to live in milliseconds of a hash key's field(s) that have an associated expiration.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HPTTL_History,0,HPTTL_Tips,0,hpttlCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HPTTL_Keyspecs,1,NULL,2),.args=HPTTL_Args,.member_key_index=4}, {MAKE_CMD("hrandfield","Returns one or more random fields from a hash.","O(N) where N is the number of fields returned","6.2.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HRANDFIELD_History,0,HRANDFIELD_Tips,1,hrandfieldCommand,-2,CMD_READONLY,ACL_CATEGORY_HASH|ACL_CATEGORY_READ|ACL_CATEGORY_SLOW,NULL,HRANDFIELD_Keyspecs,1,NULL,2),.args=HRANDFIELD_Args}, {MAKE_CMD("hscan","Iterates over fields and values of a hash.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSCAN_History,0,HSCAN_Tips,1,hscanCommand,-3,CMD_READONLY,ACL_CATEGORY_HASH|ACL_CATEGORY_READ|ACL_CATEGORY_SLOW,NULL,HSCAN_Keyspecs,1,NULL,5),.args=HSCAN_Args}, -{MAKE_CMD("hset","Creates or modifies the value of a field in a hash.","O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSET_History,1,HSET_Tips,0,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HSET_Keyspecs,1,NULL,2),.args=HSET_Args}, +{MAKE_CMD("hset","Creates or modifies the value of a field in a hash.","O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSET_History,1,HSET_Tips,0,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HSET_Keyspecs,1,NULL,2),.args=HSET_Args,.member_key_index=2,.member_key_step=2}, {MAKE_CMD("hsetex","Sets the value of one or more fields of a given hash key, and optionally sets their expiration time.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSETEX_History,0,HSETEX_Tips,0,hsetexCommand,-6,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HSETEX_Keyspecs,1,NULL,5),.args=HSETEX_Args}, -{MAKE_CMD("hsetnx","Sets the value of a field in a hash only when the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSETNX_History,0,HSETNX_Tips,0,hsetnxCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HSETNX_Keyspecs,1,NULL,3),.args=HSETNX_Args}, -{MAKE_CMD("hstrlen","Returns the length of the value of a field.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSTRLEN_History,0,HSTRLEN_Tips,0,hstrlenCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HSTRLEN_Keyspecs,1,NULL,2),.args=HSTRLEN_Args}, -{MAKE_CMD("httl","Returns the remaining time to live (in seconds) of a hash key's field(s) that have an associated expiration.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HTTL_History,0,HTTL_Tips,0,httlCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HTTL_Keyspecs,1,NULL,2),.args=HTTL_Args}, +{MAKE_CMD("hsetnx","Sets the value of a field in a hash only when the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSETNX_History,0,HSETNX_Tips,0,hsetnxCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_WRITE,NULL,HSETNX_Keyspecs,1,NULL,3),.args=HSETNX_Args,.member_key_index=2,.member_key_count=1}, +{MAKE_CMD("hstrlen","Returns the length of the value of a field.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSTRLEN_History,0,HSTRLEN_Tips,0,hstrlenCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HSTRLEN_Keyspecs,1,NULL,2),.args=HSTRLEN_Args,.member_key_index=2}, +{MAKE_CMD("httl","Returns the remaining time to live (in seconds) of a hash key's field(s) that have an associated expiration.","O(N) where N is the number of specified fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HTTL_History,0,HTTL_Tips,0,httlCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HASH|ACL_CATEGORY_READ,NULL,HTTL_Keyspecs,1,NULL,2),.args=HTTL_Args,.member_key_index=4}, {MAKE_CMD("hvals","Returns all values in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HVALS_History,0,HVALS_Tips,1,hvalsCommand,2,CMD_READONLY,ACL_CATEGORY_HASH|ACL_CATEGORY_READ|ACL_CATEGORY_SLOW,NULL,HVALS_Keyspecs,1,NULL,1),.args=HVALS_Args}, /* hyperloglog */ {MAKE_CMD("pfadd","Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.","O(1) to add every element.","2.8.9",CMD_DOC_NONE,NULL,NULL,"hyperloglog",COMMAND_GROUP_HYPERLOGLOG,PFADD_History,0,PFADD_Tips,0,pfaddCommand,-2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_HYPERLOGLOG|ACL_CATEGORY_WRITE,NULL,PFADD_Keyspecs,1,NULL,2),.args=PFADD_Args}, @@ -12050,13 +12050,13 @@ struct COMMAND_STRUCT serverCommandTable[] = { {MAKE_CMD("zcount","Returns the count of members in a sorted set that have scores within a range.","O(log(N)) with N being the number of elements in the sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZCOUNT_History,0,ZCOUNT_Tips,0,zcountCommand,4,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZCOUNT_Keyspecs,1,NULL,3),.args=ZCOUNT_Args}, {MAKE_CMD("zdiff","Returns the difference between multiple sorted sets.","O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZDIFF_History,0,ZDIFF_Tips,0,zdiffCommand,-3,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZDIFF_Keyspecs,1,zunionInterDiffGetKeys,3),.args=ZDIFF_Args}, {MAKE_CMD("zdiffstore","Stores the difference of multiple sorted sets in a key.","O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZDIFFSTORE_History,0,ZDIFFSTORE_Tips,0,zdiffstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZDIFFSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,3),.args=ZDIFFSTORE_Args}, -{MAKE_CMD("zincrby","Increments the score of a member in a sorted set.","O(log(N)) where N is the number of elements in the sorted set.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINCRBY_History,0,ZINCRBY_Tips,0,zincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZINCRBY_Keyspecs,1,NULL,3),.args=ZINCRBY_Args}, +{MAKE_CMD("zincrby","Increments the score of a member in a sorted set.","O(log(N)) where N is the number of elements in the sorted set.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINCRBY_History,0,ZINCRBY_Tips,0,zincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZINCRBY_Keyspecs,1,NULL,3),.args=ZINCRBY_Args,.member_key_index=3,.member_key_count=1}, {MAKE_CMD("zinter","Returns the intersect of multiple sorted sets.","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTER_History,0,ZINTER_Tips,0,zinterCommand,-3,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZINTER_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZINTER_Args}, {MAKE_CMD("zintercard","Returns the number of members of the intersect of multiple sorted sets.","O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.","7.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTERCARD_History,0,ZINTERCARD_Tips,0,zinterCardCommand,-3,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZINTERCARD_Keyspecs,1,zunionInterDiffGetKeys,3),.args=ZINTERCARD_Args}, {MAKE_CMD("zinterstore","Stores the intersect of multiple sorted sets in a key.","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTERSTORE_History,0,ZINTERSTORE_Tips,0,zinterstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZINTERSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZINTERSTORE_Args}, {MAKE_CMD("zlexcount","Returns the number of members in a sorted set within a lexicographical range.","O(log(N)) with N being the number of elements in the sorted set.","2.8.9",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZLEXCOUNT_History,0,ZLEXCOUNT_Tips,0,zlexcountCommand,4,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZLEXCOUNT_Keyspecs,1,NULL,3),.args=ZLEXCOUNT_Args}, {MAKE_CMD("zmpop","Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.","O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.","7.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZMPOP_History,0,ZMPOP_Tips,0,zmpopCommand,-4,CMD_WRITE,ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZMPOP_Keyspecs,1,zmpopGetKeys,4),.args=ZMPOP_Args}, -{MAKE_CMD("zmscore","Returns the score of one or more members in a sorted set.","O(N) where N is the number of members being requested.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZMSCORE_History,0,ZMSCORE_Tips,0,zmscoreCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZMSCORE_Keyspecs,1,NULL,2),.args=ZMSCORE_Args}, +{MAKE_CMD("zmscore","Returns the score of one or more members in a sorted set.","O(N) where N is the number of members being requested.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZMSCORE_History,0,ZMSCORE_Tips,0,zmscoreCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZMSCORE_Keyspecs,1,NULL,2),.args=ZMSCORE_Args,.member_key_index=2}, {MAKE_CMD("zpopmax","Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.","O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.","5.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZPOPMAX_History,0,ZPOPMAX_Tips,0,zpopmaxCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZPOPMAX_Keyspecs,1,NULL,2),.args=ZPOPMAX_Args}, {MAKE_CMD("zpopmin","Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.","O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.","5.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZPOPMIN_History,0,ZPOPMIN_Tips,0,zpopminCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZPOPMIN_Keyspecs,1,NULL,2),.args=ZPOPMIN_Args}, {MAKE_CMD("zrandmember","Returns one or more random members from a sorted set.","O(N) where N is the number of members returned","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANDMEMBER_History,0,ZRANDMEMBER_Tips,1,zrandmemberCommand,-2,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZRANDMEMBER_Keyspecs,1,NULL,2),.args=ZRANDMEMBER_Args}, @@ -12064,17 +12064,17 @@ struct COMMAND_STRUCT serverCommandTable[] = { {MAKE_CMD("zrangebylex","Returns members in a sorted set within a lexicographical range.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.8.9",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANGEBYLEX_History,0,ZRANGEBYLEX_Tips,0,zrangebylexCommand,-4,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZRANGEBYLEX_Keyspecs,1,NULL,4),.args=ZRANGEBYLEX_Args}, {MAKE_CMD("zrangebyscore","Returns members in a sorted set within a range of scores.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","1.0.5",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANGEBYSCORE_History,1,ZRANGEBYSCORE_Tips,0,zrangebyscoreCommand,-4,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZRANGEBYSCORE_Keyspecs,1,NULL,5),.args=ZRANGEBYSCORE_Args}, {MAKE_CMD("zrangestore","Stores a range of members from sorted set in a key.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANGESTORE_History,0,ZRANGESTORE_Tips,0,zrangestoreCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZRANGESTORE_Keyspecs,2,NULL,7),.args=ZRANGESTORE_Args}, -{MAKE_CMD("zrank","Returns the index of a member in a sorted set ordered by ascending scores.","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANK_History,1,ZRANK_Tips,0,zrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZRANK_Keyspecs,1,NULL,3),.args=ZRANK_Args}, -{MAKE_CMD("zrem","Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.","O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREM_History,1,ZREM_Tips,0,zremCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZREM_Keyspecs,1,NULL,2),.args=ZREM_Args}, +{MAKE_CMD("zrank","Returns the index of a member in a sorted set ordered by ascending scores.","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANK_History,1,ZRANK_Tips,0,zrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZRANK_Keyspecs,1,NULL,3),.args=ZRANK_Args,.member_key_index=2,.member_key_count=1}, +{MAKE_CMD("zrem","Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.","O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREM_History,1,ZREM_Tips,0,zremCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZREM_Keyspecs,1,NULL,2),.args=ZREM_Args,.member_key_index=2}, {MAKE_CMD("zremrangebylex","Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","2.8.9",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREMRANGEBYLEX_History,0,ZREMRANGEBYLEX_Tips,0,zremrangebylexCommand,4,CMD_WRITE,ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZREMRANGEBYLEX_Keyspecs,1,NULL,3),.args=ZREMRANGEBYLEX_Args}, {MAKE_CMD("zremrangebyrank","Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREMRANGEBYRANK_History,0,ZREMRANGEBYRANK_Tips,0,zremrangebyrankCommand,4,CMD_WRITE,ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZREMRANGEBYRANK_Keyspecs,1,NULL,3),.args=ZREMRANGEBYRANK_Args}, {MAKE_CMD("zremrangebyscore","Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREMRANGEBYSCORE_History,0,ZREMRANGEBYSCORE_Tips,0,zremrangebyscoreCommand,4,CMD_WRITE,ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZREMRANGEBYSCORE_Keyspecs,1,NULL,3),.args=ZREMRANGEBYSCORE_Args}, {MAKE_CMD("zrevrange","Returns members in a sorted set within a range of indexes in reverse order.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANGE_History,0,ZREVRANGE_Tips,0,zrevrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZREVRANGE_Keyspecs,1,NULL,4),.args=ZREVRANGE_Args}, {MAKE_CMD("zrevrangebylex","Returns members in a sorted set within a lexicographical range in reverse order.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.8.9",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANGEBYLEX_History,0,ZREVRANGEBYLEX_Tips,0,zrevrangebylexCommand,-4,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZREVRANGEBYLEX_Keyspecs,1,NULL,4),.args=ZREVRANGEBYLEX_Args}, {MAKE_CMD("zrevrangebyscore","Returns members in a sorted set within a range of scores in reverse order.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANGEBYSCORE_History,1,ZREVRANGEBYSCORE_Tips,0,zrevrangebyscoreCommand,-4,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZREVRANGEBYSCORE_Keyspecs,1,NULL,5),.args=ZREVRANGEBYSCORE_Args}, -{MAKE_CMD("zrevrank","Returns the index of a member in a sorted set ordered by descending scores.","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANK_History,1,ZREVRANK_Tips,0,zrevrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZREVRANK_Keyspecs,1,NULL,3),.args=ZREVRANK_Args}, +{MAKE_CMD("zrevrank","Returns the index of a member in a sorted set ordered by descending scores.","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANK_History,1,ZREVRANK_Tips,0,zrevrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZREVRANK_Keyspecs,1,NULL,3),.args=ZREVRANK_Args,.member_key_index=2,.member_key_count=1}, {MAKE_CMD("zscan","Iterates over members and scores of a sorted set.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCAN_History,1,ZSCAN_Tips,1,zscanCommand,-3,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZSCAN_Keyspecs,1,NULL,5),.args=ZSCAN_Args}, -{MAKE_CMD("zscore","Returns the score of a member in a sorted set.","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCORE_History,0,ZSCORE_Tips,0,zscoreCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZSCORE_Keyspecs,1,NULL,2),.args=ZSCORE_Args}, +{MAKE_CMD("zscore","Returns the score of a member in a sorted set.","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCORE_History,0,ZSCORE_Tips,0,zscoreCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_FAST|ACL_CATEGORY_READ|ACL_CATEGORY_SORTEDSET,NULL,ZSCORE_Keyspecs,1,NULL,2),.args=ZSCORE_Args,.member_key_index=2}, {MAKE_CMD("zunion","Returns the union of multiple sorted sets.","O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNION_History,0,ZUNION_Tips,0,zunionCommand,-3,CMD_READONLY,ACL_CATEGORY_READ|ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET,NULL,ZUNION_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZUNION_Args}, {MAKE_CMD("zunionstore","Stores the union of multiple sorted sets in a key.","O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNIONSTORE_History,0,ZUNIONSTORE_Tips,0,zunionstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SLOW|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_WRITE,NULL,ZUNIONSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZUNIONSTORE_Args}, /* stream */ diff --git a/src/commands/hdel.json b/src/commands/hdel.json index 39ad2b84c0d..dc9d297fc69 100644 --- a/src/commands/hdel.json +++ b/src/commands/hdel.json @@ -5,6 +5,7 @@ "group": "hash", "since": "2.0.0", "arity": -3, + "member_key_index": 2, "function": "hdelCommand", "history": [ [ diff --git a/src/commands/hexists.json b/src/commands/hexists.json index 0403e611781..a5ac1b44abd 100644 --- a/src/commands/hexists.json +++ b/src/commands/hexists.json @@ -5,6 +5,7 @@ "group": "hash", "since": "2.0.0", "arity": 3, + "member_key_index": 2, "function": "hexistsCommand", "command_flags": [ "READONLY", diff --git a/src/commands/hexpiretime.json b/src/commands/hexpiretime.json index 24c9084302a..36d3b41e129 100644 --- a/src/commands/hexpiretime.json +++ b/src/commands/hexpiretime.json @@ -5,6 +5,7 @@ "group": "hash", "since": "9.0.0", "arity": -5, + "member_key_index": 4, "function": "hexpiretimeCommand", "command_flags": [ "READONLY", diff --git a/src/commands/hget.json b/src/commands/hget.json index b81b03a9ac6..0b977b8d9b6 100644 --- a/src/commands/hget.json +++ b/src/commands/hget.json @@ -5,6 +5,7 @@ "group": "hash", "since": "2.0.0", "arity": 3, + "member_key_index": 2, "function": "hgetCommand", "command_flags": [ "READONLY", diff --git a/src/commands/hgetdel.json b/src/commands/hgetdel.json index fdec47bb2e7..d424a425f8b 100644 --- a/src/commands/hgetdel.json +++ b/src/commands/hgetdel.json @@ -5,6 +5,7 @@ "group": "hash", "since": "9.1.0", "arity": -5, + "member_key_index": 4, "function": "hgetdelCommand", "command_flags": [ "WRITE", diff --git a/src/commands/hincrby.json b/src/commands/hincrby.json index ddc42fc3082..75fbabcd973 100644 --- a/src/commands/hincrby.json +++ b/src/commands/hincrby.json @@ -5,6 +5,8 @@ "group": "hash", "since": "2.0.0", "arity": 4, + "member_key_index": 2, + "member_key_count": 1, "function": "hincrbyCommand", "command_flags": [ "WRITE", diff --git a/src/commands/hincrbyfloat.json b/src/commands/hincrbyfloat.json index 91cf9452f47..4f9955b423e 100644 --- a/src/commands/hincrbyfloat.json +++ b/src/commands/hincrbyfloat.json @@ -5,6 +5,8 @@ "group": "hash", "since": "2.6.0", "arity": 4, + "member_key_index": 2, + "member_key_count": 1, "function": "hincrbyfloatCommand", "command_flags": [ "WRITE", diff --git a/src/commands/hmget.json b/src/commands/hmget.json index 22bb670dfc8..9b9e5ab6192 100644 --- a/src/commands/hmget.json +++ b/src/commands/hmget.json @@ -5,6 +5,7 @@ "group": "hash", "since": "2.0.0", "arity": -3, + "member_key_index": 2, "function": "hmgetCommand", "command_flags": [ "READONLY", diff --git a/src/commands/hmset.json b/src/commands/hmset.json index dbeabebcb4e..f9dc38fc8d7 100644 --- a/src/commands/hmset.json +++ b/src/commands/hmset.json @@ -5,6 +5,8 @@ "group": "hash", "since": "2.0.0", "arity": -4, + "member_key_index": 2, + "member_key_step": 2, "function": "hsetCommand", "command_flags": [ "WRITE", diff --git a/src/commands/hpersist.json b/src/commands/hpersist.json index 59ae09a1239..7fb7e15d130 100644 --- a/src/commands/hpersist.json +++ b/src/commands/hpersist.json @@ -5,6 +5,7 @@ "group": "hash", "since": "9.0.0", "arity": -5, + "member_key_index": 4, "function": "hpersistCommand", "command_flags": [ "WRITE", diff --git a/src/commands/hpexpiretime.json b/src/commands/hpexpiretime.json index 2156b72fd7c..964fff5b525 100644 --- a/src/commands/hpexpiretime.json +++ b/src/commands/hpexpiretime.json @@ -5,6 +5,7 @@ "group": "hash", "since": "9.0.0", "arity": -5, + "member_key_index": 4, "function": "hpexpiretimeCommand", "command_flags": [ "READONLY", diff --git a/src/commands/hpttl.json b/src/commands/hpttl.json index 9910b77c5a5..f2dd712f245 100644 --- a/src/commands/hpttl.json +++ b/src/commands/hpttl.json @@ -5,6 +5,7 @@ "group": "hash", "since": "9.0.0", "arity": -5, + "member_key_index": 4, "function": "hpttlCommand", "command_flags": [ "READONLY", diff --git a/src/commands/hset.json b/src/commands/hset.json index 6b0dbec96cd..f0f2db4a7b8 100644 --- a/src/commands/hset.json +++ b/src/commands/hset.json @@ -5,6 +5,8 @@ "group": "hash", "since": "2.0.0", "arity": -4, + "member_key_index": 2, + "member_key_step": 2, "function": "hsetCommand", "history": [ [ diff --git a/src/commands/hsetnx.json b/src/commands/hsetnx.json index fcc61afaf8a..5d92448e863 100644 --- a/src/commands/hsetnx.json +++ b/src/commands/hsetnx.json @@ -5,6 +5,8 @@ "group": "hash", "since": "2.0.0", "arity": 4, + "member_key_index": 2, + "member_key_count": 1, "function": "hsetnxCommand", "command_flags": [ "WRITE", diff --git a/src/commands/hstrlen.json b/src/commands/hstrlen.json index 35bf8ae9dc0..ba3a8d0cd16 100644 --- a/src/commands/hstrlen.json +++ b/src/commands/hstrlen.json @@ -5,6 +5,7 @@ "group": "hash", "since": "3.2.0", "arity": 3, + "member_key_index": 2, "function": "hstrlenCommand", "command_flags": [ "READONLY", diff --git a/src/commands/httl.json b/src/commands/httl.json index e39e15e7547..cb7eb944a7a 100644 --- a/src/commands/httl.json +++ b/src/commands/httl.json @@ -5,6 +5,7 @@ "group": "hash", "since": "9.0.0", "arity": -5, + "member_key_index": 4, "function": "httlCommand", "command_flags": [ "READONLY", diff --git a/src/commands/zincrby.json b/src/commands/zincrby.json index 453e18bc923..4d998c406c8 100644 --- a/src/commands/zincrby.json +++ b/src/commands/zincrby.json @@ -5,6 +5,8 @@ "group": "sorted_set", "since": "1.2.0", "arity": 4, + "member_key_index": 3, + "member_key_count": 1, "function": "zincrbyCommand", "command_flags": [ "WRITE", diff --git a/src/commands/zmscore.json b/src/commands/zmscore.json index c08f699c340..98eec09fdec 100644 --- a/src/commands/zmscore.json +++ b/src/commands/zmscore.json @@ -5,6 +5,7 @@ "group": "sorted_set", "since": "6.2.0", "arity": -3, + "member_key_index": 2, "function": "zmscoreCommand", "command_flags": [ "READONLY", diff --git a/src/commands/zrank.json b/src/commands/zrank.json index 34491fecaba..617a7ba8387 100644 --- a/src/commands/zrank.json +++ b/src/commands/zrank.json @@ -5,6 +5,8 @@ "group": "sorted_set", "since": "2.0.0", "arity": -3, + "member_key_index": 2, + "member_key_count": 1, "function": "zrankCommand", "history": [ [ diff --git a/src/commands/zrem.json b/src/commands/zrem.json index c372f486806..cf4ca215836 100644 --- a/src/commands/zrem.json +++ b/src/commands/zrem.json @@ -5,6 +5,7 @@ "group": "sorted_set", "since": "1.2.0", "arity": -3, + "member_key_index": 2, "function": "zremCommand", "history": [ [ diff --git a/src/commands/zrevrank.json b/src/commands/zrevrank.json index 411a4c169e9..27dbaacad37 100644 --- a/src/commands/zrevrank.json +++ b/src/commands/zrevrank.json @@ -5,6 +5,8 @@ "group": "sorted_set", "since": "2.0.0", "arity": -3, + "member_key_index": 2, + "member_key_count": 1, "function": "zrevrankCommand", "history": [ [ diff --git a/src/commands/zscore.json b/src/commands/zscore.json index 987c6999701..47e93e617c4 100644 --- a/src/commands/zscore.json +++ b/src/commands/zscore.json @@ -5,6 +5,7 @@ "group": "sorted_set", "since": "1.2.0", "arity": 3, + "member_key_index": 2, "function": "zscoreCommand", "command_flags": [ "READONLY", diff --git a/src/memory_prefetch.c b/src/memory_prefetch.c index 963514937c8..7faf14000e8 100644 --- a/src/memory_prefetch.c +++ b/src/memory_prefetch.c @@ -13,14 +13,31 @@ #include "io_threads.h" typedef enum { - PREFETCH_ENTRY, /* Initial state, prefetch entries associated with the given key's hash */ - PREFETCH_VALUE, /* prefetch the value object of the entry found in the previous step */ - PREFETCH_DONE /* Indicates that prefetching for this key is complete */ + PREFETCH_ENTRY, /* Initial state, prefetch entries associated with the given key's hash */ + PREFETCH_VALUE, /* prefetch the value object of the entry found in the previous step */ + PREFETCH_VALUE_DEEP, /* deep prefetch of inner hashtable for hash/zset types */ + PREFETCH_DONE /* Indicates that prefetching for this key is complete */ } PrefetchState; +typedef enum { + DEEP_PREFETCH_HEADER, /* Prefetch val->ptr (data structure header) */ + DEEP_PREFETCH_INIT, /* Init incremental find on inner hashtable */ + DEEP_PREFETCH_STEP, /* Step through incremental find */ +} DeepPrefetchPhase; + typedef struct KeyPrefetchInfo { PrefetchState state; /* Current state of the prefetch operation */ hashtableIncrementalFindState hashtab_state; + /* Fields for deep prefetching of inner hashtables (hash/zset) */ + robj **argv; /* argv of the command that owns this key */ + int argc; /* argc of the command that owns this key */ + int member_key_index; /* argv index of the first field/member */ + int member_key_step; /* stride between fields (1 = consecutive, 2 = interleaved) */ + int member_key_count; /* max fields to prefetch (-1 = all remaining) */ + int current_field_idx; /* current argv index being prefetched */ + hashtable *inner_ht; /* cached inner hashtable pointer */ + DeepPrefetchPhase deep_phase; + hashtableIncrementalFindState inner_hashtab_state; } KeyPrefetchInfo; /* PrefetchCommandsBatch structure holds the state of the current batch of client commands being processed. */ @@ -34,6 +51,11 @@ typedef struct PrefetchCommandsBatch { int *slots; /* Array of slots for each key */ void **keys; /* Array of keys to prefetch in the current batch */ client **clients; /* Array of clients in the current batch */ + robj ***key_argv; /* Per-key argv pointer (correct for queued commands) */ + int *key_argc; /* Per-key argc (correct for queued commands) */ + int *key_member_indices; /* member_key_index from cmd for each key (0 = no deep prefetch) */ + int *key_member_steps; /* member_key_step from cmd for each key */ + int *key_member_counts; /* member_key_count from cmd for each key */ hashtable **keys_tables; /* Main table for each key */ KeyPrefetchInfo *prefetch_info; /* Prefetch info for each key */ } PrefetchCommandsBatch; @@ -46,6 +68,11 @@ void freePrefetchCommandsBatch(void) { } zfree(batch->clients); + zfree(batch->key_argv); + zfree(batch->key_argc); + zfree(batch->key_member_indices); + zfree(batch->key_member_steps); + zfree(batch->key_member_counts); zfree(batch->keys); zfree(batch->keys_tables); zfree(batch->slots); @@ -65,6 +92,11 @@ void prefetchCommandsBatchInit(void) { batch = zcalloc(sizeof(PrefetchCommandsBatch)); batch->max_prefetch_size = max_prefetch_size; batch->clients = zcalloc(max_prefetch_size * sizeof(client *)); + batch->key_argv = zcalloc(max_prefetch_size * sizeof(robj **)); + batch->key_argc = zcalloc(max_prefetch_size * sizeof(int)); + batch->key_member_indices = zcalloc(max_prefetch_size * sizeof(int)); + batch->key_member_steps = zcalloc(max_prefetch_size * sizeof(int)); + batch->key_member_counts = zcalloc(max_prefetch_size * sizeof(int)); batch->keys = zcalloc(max_prefetch_size * sizeof(void *)); batch->keys_tables = zcalloc(max_prefetch_size * sizeof(hashtable *)); batch->slots = zcalloc(max_prefetch_size * sizeof(int)); @@ -115,18 +147,38 @@ static void initBatchInfo(hashtable **tables) { continue; } info->state = PREFETCH_ENTRY; + info->argv = batch->key_argv[i]; + info->argc = batch->key_argc[i]; + info->member_key_index = batch->key_member_indices[i]; + info->member_key_step = batch->key_member_steps[i]; + info->member_key_count = batch->key_member_counts[i]; + info->current_field_idx = 0; + info->inner_ht = NULL; + info->deep_phase = DEEP_PREFETCH_HEADER; hashtableIncrementalFindInit(&info->hashtab_state, tables[i], batch->keys[i]); } } +/* Check if a value type supports deep prefetching of its inner hashtable. */ +static inline int canDeepPrefetch(robj *val) { + return val->encoding == OBJ_ENCODING_HASHTABLE || val->encoding == OBJ_ENCODING_SKIPLIST; +} + static void prefetchEntry(KeyPrefetchInfo *info) { if (hashtableIncrementalFindStep(&info->hashtab_state)) { /* Not done yet */ moveToNextKey(); } else if (server.io_threads_num >= server.min_io_threads_copy_avoid) { - /* Copy avoidance should be more efficient without value prefetch - * starting certain number of I/O threads */ - markKeyAsdone(info); + /* Copy avoidance skips PREFETCH_VALUE for strings, but we still want + * deep prefetch for hash/zset inner hashtables. Check if applicable. */ + void *entry; + if (hashtableIncrementalFindGetResult(&info->hashtab_state, &entry) && + info->member_key_index > 0 && + info->argc > info->member_key_index && canDeepPrefetch(entry)) { + info->state = PREFETCH_VALUE_DEEP; + } else { + markKeyAsdone(info); + } } else { info->state = PREFETCH_VALUE; } @@ -140,11 +192,81 @@ static void prefetchValue(KeyPrefetchInfo *info) { if (val->encoding == OBJ_ENCODING_RAW && val->type == OBJ_STRING) { valkey_prefetch(objectGetVal(val)); } + /* For types with inner hashtables, transition to deep prefetch */ + if (info->member_key_index > 0 && + info->argc > info->member_key_index && canDeepPrefetch(val)) { + info->state = PREFETCH_VALUE_DEEP; + return; + } } markKeyAsdone(info); } +/* Deep prefetch: walk the inner hashtable for hash/zset types. + * Uses a 3-phase approach (HEADER -> INIT -> STEP) to amortize cache misses + * across multiple commands in the batch. Supports multiple fields per command + * using member_key_step and member_key_count. */ +static void prefetchValueDeep(KeyPrefetchInfo *info) { + void *entry; + if (!hashtableIncrementalFindGetResult(&info->hashtab_state, &entry)) { + markKeyAsdone(info); + return; + } + robj *val = entry; + + switch (info->deep_phase) { + case DEEP_PREFETCH_HEADER: + /* Phase 1: Prefetch the data structure header (val->ptr) */ + valkey_prefetch(objectGetVal(val)); + info->deep_phase = DEEP_PREFETCH_INIT; + info->current_field_idx = info->member_key_index; + moveToNextKey(); + return; + + case DEEP_PREFETCH_INIT: { + /* Phase 2: Header is warm. Get inner hashtable and init incremental find. */ + if (!info->inner_ht) { + if (val->encoding == OBJ_ENCODING_HASHTABLE) { + info->inner_ht = objectGetVal(val); + } else if (val->encoding == OBJ_ENCODING_SKIPLIST) { + zset *zs = objectGetVal(val); + info->inner_ht = zs->ht; + } + if (!info->inner_ht || hashtableSize(info->inner_ht) == 0) { + markKeyAsdone(info); + return; + } + } + hashtableIncrementalFindInit(&info->inner_hashtab_state, info->inner_ht, + objectGetVal(info->argv[info->current_field_idx])); + info->deep_phase = DEEP_PREFETCH_STEP; + moveToNextKey(); + return; + } + + case DEEP_PREFETCH_STEP: + /* Phase 3: Step through the inner hashtable find */ + if (hashtableIncrementalFindStep(&info->inner_hashtab_state)) { + moveToNextKey(); + return; + } + /* Current field done. Check if there are more fields to prefetch. */ + int next_idx = info->current_field_idx + info->member_key_step; + int fields_done = (next_idx - info->member_key_index) / info->member_key_step; + if (next_idx < info->argc && + (info->member_key_count < 0 || fields_done < info->member_key_count)) { + /* More fields to prefetch - loop back to INIT */ + info->current_field_idx = next_idx; + info->deep_phase = DEEP_PREFETCH_INIT; + moveToNextKey(); + return; + } + markKeyAsdone(info); + return; + } +} + /* Prefetch hashtable data for an array of keys. * * This function takes an array of tables and keys, attempting to bring @@ -162,6 +284,7 @@ static void hashtablePrefetch(hashtable **tables) { switch (info->state) { case PREFETCH_ENTRY: prefetchEntry(info); break; case PREFETCH_VALUE: prefetchValue(info); break; + case PREFETCH_VALUE_DEEP: prefetchValueDeep(info); break; default: serverPanic("Unknown prefetch state %d", info->state); } } @@ -251,6 +374,11 @@ static void addCommandToBatch(struct serverCommand *cmd, robj **argv, int argc, batch->keys[batch->key_count] = argv[result.keys[i].pos]; batch->slots[batch->key_count] = slot >= 0 ? slot : 0; batch->keys_tables[batch->key_count] = kvstoreGetHashtable(db->keys, batch->slots[batch->key_count]); + batch->key_argv[batch->key_count] = argv; + batch->key_argc[batch->key_count] = argc; + batch->key_member_indices[batch->key_count] = cmd->member_key_index; + batch->key_member_steps[batch->key_count] = cmd->member_key_step ? cmd->member_key_step : 1; + batch->key_member_counts[batch->key_count] = cmd->member_key_count ? cmd->member_key_count : -1; batch->key_count++; } getKeysFreeResult(&result); diff --git a/src/server.h b/src/server.h index 8702ed222e7..99c787703df 100644 --- a/src/server.h +++ b/src/server.h @@ -2699,6 +2699,11 @@ struct serverCommand { * Used for Cluster redirect (may be NULL) */ serverGetKeysProc *getkeys_proc; int num_args; /* Length of args array. */ + /* Deep prefetch: argv index of the first field/member for inner hashtable lookup. + * 0 means disabled. Used by the prefetch system to find the lookup key. */ + int member_key_index; + int member_key_step; /* stride between fields (default 1) */ + int member_key_count; /* max fields to prefetch (-1 = all remaining, 0 = use default) */ /* Array of subcommands (may be NULL) */ struct serverCommand *subcommands; /* Array of arguments (may be NULL) */ diff --git a/tests/unit/deep-prefetch.tcl b/tests/unit/deep-prefetch.tcl new file mode 100644 index 00000000000..b386d3240bc --- /dev/null +++ b/tests/unit/deep-prefetch.tcl @@ -0,0 +1,72 @@ +# Test deep prefetch for hash and zset inner hashtables. +# Requires io-threads >= 2 and hashtable-encoded objects (>128 fields). + +start_server {config "minimal.conf" tags {"external:skip"} overrides {io-threads 4 hash-max-listpack-entries 0 zset-max-listpack-entries 0}} { + test "Deep prefetch - HGET correctness with pipelined commands" { + # Create a hash with enough fields to use hashtable encoding + for {set i 0} {$i < 200} {incr i} { + r hset myhash "field:$i" "value:$i" + } + assert_encoding hashtable myhash + + # Pipeline multiple HGET commands to trigger batched prefetch + set rd [valkey_deferring_client] + for {set i 0} {$i < 50} {incr i} { + $rd hget myhash "field:$i" + } + $rd flush + for {set i 0} {$i < 50} {incr i} { + assert_equal "value:$i" [$rd read] + } + $rd close + } + + test "Deep prefetch - ZSCORE correctness with pipelined commands" { + # Create a zset with enough members to use skiplist encoding + for {set i 0} {$i < 200} {incr i} { + r zadd myzset $i "member:$i" + } + assert_encoding skiplist myzset + + # Pipeline multiple ZSCORE commands + set rd [valkey_deferring_client] + for {set i 0} {$i < 50} {incr i} { + $rd zscore myzset "member:$i" + } + $rd flush + for {set i 0} {$i < 50} {incr i} { + assert_equal $i [$rd read] + } + $rd close + } + + test "Deep prefetch - HMGET multi-field correctness" { + set rd [valkey_deferring_client] + $rd hmget myhash field:0 field:1 field:2 field:3 field:4 + $rd flush + set result [$rd read] + assert_equal [list value:0 value:1 value:2 value:3 value:4] $result + $rd close + } + + test "Deep prefetch - mixed commands in pipeline" { + set rd [valkey_deferring_client] + $rd hget myhash field:10 + $rd zscore myzset member:10 + $rd hdel myhash field:199 + $rd hget myhash field:199 + $rd flush + assert_equal "value:10" [$rd read] + assert_equal 10 [$rd read] + assert_equal 1 [$rd read] + assert_equal {} [$rd read] + $rd close + } + + test "Deep prefetch - prefetch stats non-negative" { + # Just verify the stats exist and are non-negative (prefetch may not + # trigger in test env with single client, but should never be negative) + set entries [getInfoProperty [r info stats] io_threaded_total_prefetch_entries] + assert {$entries >= 0} + } +} diff --git a/utils/generate-command-code.py b/utils/generate-command-code.py index 63ebd1ae680..6b9d37b003e 100755 --- a/utils/generate-command-code.py +++ b/utils/generate-command-code.py @@ -496,6 +496,13 @@ def _doc_flags_code(): if self.args: s += ".args=%s," % self.arg_table_name() + if self.desc.get("member_key_index"): + s += ".member_key_index=%d," % self.desc["member_key_index"] + if self.desc.get("member_key_step"): + s += ".member_key_step=%d," % self.desc["member_key_step"] + if self.desc.get("member_key_count"): + s += ".member_key_count=%d," % self.desc["member_key_count"] + if self.reply_schema and args.with_reply_schema: s += ".reply_schema=&%s," % self.reply_schema_name()