From e8901b2fe489013b17e943d2721f961a50bda07c Mon Sep 17 00:00:00 2001
From: "zhaozhao.zz" <zhaozhao.zz@alibaba-inc.com>
Date: Fri, 8 Dec 2017 15:37:08 +0800
Subject: [PATCH 1/8] zset: fix the int problem

---
 src/server.h |  2 +-
 src/t_zset.c | 22 +++++++++++++---------
 2 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/src/server.h b/src/server.h
index ee3b7df5..9917c1f8 100644
--- a/src/server.h
+++ b/src/server.h
@@ -1590,7 +1590,7 @@ void zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
 void zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
 unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);
 unsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range);
-unsigned int zsetLength(const robj *zobj);
+unsigned long zsetLength(const robj *zobj);
 void zsetConvert(robj *zobj, int encoding);
 void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen);
 int zsetScore(robj *zobj, sds member, double *score);
diff --git a/src/t_zset.c b/src/t_zset.c
index f7f4c6eb..cfd5f2b9 100644
--- a/src/t_zset.c
+++ b/src/t_zset.c
@@ -1100,8 +1100,8 @@ unsigned char *zzlDeleteRangeByRank(unsigned char *zl, unsigned int start, unsig
  * Common sorted set API
  *----------------------------------------------------------------------------*/
 
-unsigned int zsetLength(const robj *zobj) {
-    int length = -1;
+unsigned long zsetLength(const robj *zobj) {
+    unsigned long length = 0;
     if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
         length = zzlLength(zobj->ptr);
     } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
@@ -1878,7 +1878,7 @@ void zuiClearIterator(zsetopsrc *op) {
     }
 }
 
-int zuiLength(zsetopsrc *op) {
+unsigned long zuiLength(zsetopsrc *op) {
     if (op->subject == NULL)
         return 0;
 
@@ -2085,7 +2085,11 @@ int zuiFind(zsetopsrc *op, zsetopval *val, double *score) {
 }
 
 int zuiCompareByCardinality(const void *s1, const void *s2) {
-    return zuiLength((zsetopsrc*)s1) - zuiLength((zsetopsrc*)s2);
+    unsigned long first = zuiLength((zsetopsrc*)s1);
+    unsigned long second = zuiLength((zsetopsrc*)s2);
+    if (first > second) return 1;
+    if (first < second) return -1;
+    return 0;
 }
 
 #define REDIS_AGGR_SUM 1
@@ -2129,7 +2133,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) {
     zsetopsrc *src;
     zsetopval zval;
     sds tmp;
-    unsigned int maxelelen = 0;
+    size_t maxelelen = 0;
     robj *dstobj;
     zset *dstzset;
     zskiplistNode *znode;
@@ -2363,8 +2367,8 @@ void zrangeGenericCommand(client *c, int reverse) {
     int withscores = 0;
     long start;
     long end;
-    int llen;
-    int rangelen;
+    long llen;
+    long rangelen;
 
     if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||
         (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;
@@ -2671,7 +2675,7 @@ void zcountCommand(client *c) {
     robj *key = c->argv[1];
     robj *zobj;
     zrangespec range;
-    int count = 0;
+    unsigned long count = 0;
 
     /* Parse the range arguments */
     if (zslParseRange(c->argv[2],c->argv[3],&range) != C_OK) {
@@ -2748,7 +2752,7 @@ void zlexcountCommand(client *c) {
     robj *key = c->argv[1];
     robj *zobj;
     zlexrangespec range;
-    int count = 0;
+    unsigned long count = 0;
 
     /* Parse the range arguments */
     if (zslParseLexRange(c->argv[2],c->argv[3],&range) != C_OK) {

From 109ee497be24906e7931d33b71e3a6e78c5de77b Mon Sep 17 00:00:00 2001
From: "zhaozhao.zz" <zhaozhao.zz@alibaba-inc.com>
Date: Fri, 8 Dec 2017 16:09:27 +0800
Subject: [PATCH 2/8] zset: change the span of zskiplistNode to unsigned long

---
 src/server.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/server.h b/src/server.h
index 9917c1f8..f3a97065 100644
--- a/src/server.h
+++ b/src/server.h
@@ -335,7 +335,7 @@ typedef long long mstime_t; /* millisecond time type. */
 /* Anti-warning macro... */
 #define UNUSED(V) ((void) V)
 
-#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^32 elements */
+#define ZSKIPLIST_MAXLEVEL 64 /* Should be enough for 2^64 elements */
 #define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */
 
 /* Append only defines */
@@ -774,7 +774,7 @@ typedef struct zskiplistNode {
     struct zskiplistNode *backward;
     struct zskiplistLevel {
         struct zskiplistNode *forward;
-        unsigned int span;
+        unsigned long span;
     } level[];
 } zskiplistNode;
 

From 83cf0e3668f61a57bbaaedf47d35d8943352d893 Mon Sep 17 00:00:00 2001
From: "zhaozhao.zz" <zhaozhao.zz@alibaba-inc.com>
Date: Thu, 29 Mar 2018 17:36:15 +0800
Subject: [PATCH 3/8] adjust position of _dictNextPower in dictExpand

---
 src/dict.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/dict.c b/src/dict.c
index 97e63680..a7b77aad 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -146,14 +146,14 @@ int dictResize(dict *d)
 /* Expand or create the hash table */
 int dictExpand(dict *d, unsigned long size)
 {
-    dictht n; /* the new hash table */
-    unsigned long realsize = _dictNextPower(size);
-
     /* the size is invalid if it is smaller than the number of
      * elements already inside the hash table */
     if (dictIsRehashing(d) || d->ht[0].used > size)
         return DICT_ERR;
 
+    dictht n; /* the new hash table */
+    unsigned long realsize = _dictNextPower(size);
+
     /* Rehashing to the same table size is not useful. */
     if (realsize == d->ht[0].size) return DICT_ERR;
 

From 24036b4d32d857066a2ccfc6cef2e8a751634ad5 Mon Sep 17 00:00:00 2001
From: "zhaozhao.zz" <zhaozhao.zz@alibaba-inc.com>
Date: Sun, 22 Apr 2018 22:30:44 +0800
Subject: [PATCH 4/8] RDB: expand dict if needed when rdb load object

---
 src/rdb.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/rdb.c b/src/rdb.c
index 27c3aa78..639be1ea 100644
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -1427,6 +1427,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
         o = createZsetObject();
         zs = o->ptr;
 
+        if (zsetlen > DICT_HT_INITIAL_SIZE)
+            dictExpand(zs->dict,zsetlen);
+
         /* Load every single element of the sorted set. */
         while(zsetlen--) {
             sds sdsele;
@@ -1495,6 +1498,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
             sdsfree(value);
         }
 
+        if (o->encoding == OBJ_ENCODING_HT && len > DICT_HT_INITIAL_SIZE)
+            dictExpand(o->ptr,len);
+
         /* Load remaining fields and values into the hash table */
         while (o->encoding == OBJ_ENCODING_HT && len > 0) {
             len--;

From 1749fe7a26e6ce53ab90271aa807380bd9458d3a Mon Sep 17 00:00:00 2001
From: michael-grunder <michael.grunder@gmail.com>
Date: Sat, 2 Jun 2018 18:22:20 -0700
Subject: [PATCH 5/8] Return early in XPENDING if sent a nonexistent consumer
 group.

---
 src/t_stream.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/t_stream.c b/src/t_stream.c
index 6cbef56c..ebcf1a55 100644
--- a/src/t_stream.c
+++ b/src/t_stream.c
@@ -1728,8 +1728,10 @@ void xpendingCommand(client *c) {
 
         /* If a consumer name was mentioned but it does not exist, we can
          * just return an empty array. */
-        if (consumername && consumer == NULL)
+        if (consumername && consumer == NULL) {
             addReplyMultiBulkLen(c,0);
+            return;
+        }
 
         rax *pel = consumer ? consumer->pel : group->pel;
         unsigned char startkey[sizeof(streamID)];

From b2fc2eaecb85b34a12c2b6a4db91fa5fc466870b Mon Sep 17 00:00:00 2001
From: antirez <antirez@gmail.com>
Date: Thu, 7 Jun 2018 18:52:01 +0200
Subject: [PATCH 6/8] Add the stream group to the script generating the help.

---
 utils/generate-command-help.rb | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/utils/generate-command-help.rb b/utils/generate-command-help.rb
index f3dfb31b..29acef69 100755
--- a/utils/generate-command-help.rb
+++ b/utils/generate-command-help.rb
@@ -14,7 +14,8 @@ GROUPS = [
   "scripting",
   "hyperloglog",
   "cluster",
-  "geo"
+  "geo",
+  "stream"
 ].freeze
 
 GROUPS_BY_NAME = Hash[*

From 2268d7e5dd3a44a95f0e44ffe1afccebd8264b64 Mon Sep 17 00:00:00 2001
From: antirez <antirez@gmail.com>
Date: Thu, 7 Jun 2018 18:53:00 +0200
Subject: [PATCH 7/8] redis-cli inline help updated.

---
 src/help.h | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 107 insertions(+), 6 deletions(-)

diff --git a/src/help.h b/src/help.h
index 5f927c30..005afd94 100644
--- a/src/help.h
+++ b/src/help.h
@@ -1,4 +1,4 @@
-/* Automatically generated by utils/generate-command-help.rb, do not edit. */
+/* Automatically generated by generate-command-help.rb, do not edit. */
 
 #ifndef __REDIS_HELP_H
 #define __REDIS_HELP_H
@@ -17,7 +17,8 @@ static char *commandGroups[] = {
     "scripting",
     "hyperloglog",
     "cluster",
-    "geo"
+    "geo",
+    "stream"
 };
 
 struct commandHelp {
@@ -82,6 +83,16 @@ struct commandHelp {
     "Pop a value from a list, push it to another list and return it; or block until one is available",
     2,
     "2.2.0" },
+    { "BZPOPMAX",
+    "key [key ...] timeout",
+    "Remove and return the member with the highest score from one or more sorted sets, or block until one is available",
+    4,
+    "5.0.0" },
+    { "BZPOPMIN",
+    "key [key ...] timeout",
+    "Remove and return the member with the lowest score from one or more sorted sets, or block until one is available",
+    4,
+    "5.0.0" },
     { "CLIENT GETNAME",
     "-",
     "Get the current connection name",
@@ -318,12 +329,12 @@ struct commandHelp {
     0,
     "1.2.0" },
     { "FLUSHALL",
-    "-",
+    "[ASYNC]",
     "Remove all keys from all databases",
     9,
     "1.0.0" },
     { "FLUSHDB",
-    "-",
+    "[ASYNC]",
     "Remove all keys from the current database",
     9,
     "1.0.0" },
@@ -532,6 +543,36 @@ struct commandHelp {
     "Trim a list to the specified range",
     2,
     "1.0.0" },
+    { "MEMORY DOCTOR",
+    "-",
+    "Outputs memory problems report",
+    9,
+    "4.0.0" },
+    { "MEMORY HELP",
+    "-",
+    "Show helpful text about the different subcommands",
+    9,
+    "4.0.0" },
+    { "MEMORY MALLOC-STATS",
+    "-",
+    "Show allocator internal stats",
+    9,
+    "4.0.0" },
+    { "MEMORY PURGE",
+    "-",
+    "Ask the allocator to release memory",
+    9,
+    "4.0.0" },
+    { "MEMORY STATS",
+    "-",
+    "Show memory usage details",
+    9,
+    "4.0.0" },
+    { "MEMORY USAGE",
+    "key [SAMPLES count]",
+    "Estimate the memory usage of a key",
+    9,
+    "4.0.0" },
     { "MGET",
     "key [key ...]",
     "Get the values of all the given keys",
@@ -723,7 +764,7 @@ struct commandHelp {
     10,
     "3.2.0" },
     { "SCRIPT EXISTS",
-    "script [script ...]",
+    "sha1 [sha1 ...]",
     "Check existence of scripts in the script cache.",
     10,
     "2.6.0" },
@@ -758,7 +799,7 @@ struct commandHelp {
     8,
     "1.0.0" },
     { "SET",
-    "key value [EX seconds] [PX milliseconds] [NX|XX]",
+    "key value [expiration EX seconds|PX milliseconds] [NX|XX]",
     "Set the string value of a key",
     1,
     "1.0.0" },
@@ -867,6 +908,11 @@ struct commandHelp {
     "Add multiple sets and store the resulting set in a key",
     3,
     "1.0.0" },
+    { "SWAPDB",
+    "index index",
+    "Swaps two Redis databases",
+    8,
+    "4.0.0" },
     { "SYNC",
     "-",
     "Internal command used for replication",
@@ -877,6 +923,11 @@ struct commandHelp {
     "Return the current server time",
     9,
     "2.6.0" },
+    { "TOUCH",
+    "key [key ...]",
+    "Alters the last access time of a key(s). Returns the number of existing keys specified.",
+    0,
+    "3.2.1" },
     { "TTL",
     "key",
     "Get the time to live for a key",
@@ -887,6 +938,11 @@ struct commandHelp {
     "Determine the type stored at key",
     0,
     "1.0.0" },
+    { "UNLINK",
+    "key [key ...]",
+    "Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.",
+    0,
+    "4.0.0" },
     { "UNSUBSCRIBE",
     "[channel [channel ...]]",
     "Stop listening for messages posted to the given channels",
@@ -907,6 +963,41 @@ struct commandHelp {
     "Watch the given keys to determine execution of the MULTI/EXEC block",
     7,
     "2.2.0" },
+    { "XADD",
+    "key ID field string [field string ...]",
+    "Appends a new entry to a stream",
+    14,
+    "5.0.0" },
+    { "XLEN",
+    "key",
+    "Return the number of entires in a stream",
+    14,
+    "5.0.0" },
+    { "XPENDING",
+    "key group [start end count] [consumer]",
+    "Return information and entries from a stream conusmer group pending entries list, that are messages fetched but never acknowledged.",
+    14,
+    "5.0.0" },
+    { "XRANGE",
+    "key start end [COUNT count]",
+    "Return a range of elements in a stream, with IDs matching the specified IDs interval",
+    14,
+    "5.0.0" },
+    { "XREAD",
+    "[COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]",
+    "Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.",
+    14,
+    "5.0.0" },
+    { "XREADGROUP",
+    "GROUP group consumer [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]",
+    "Return new entries from a stream using a consumer group, or access the history of the pending entries for a given consumer. Can block.",
+    14,
+    "5.0.0" },
+    { "XREVRANGE",
+    "key end start [COUNT count]",
+    "Return a range of elements in a stream, with IDs matching the specified IDs interval, in reverse order (from greater to smaller IDs) compared to XRANGE",
+    14,
+    "5.0.0" },
     { "ZADD",
     "key [NX|XX] [CH] [INCR] score member [score member ...]",
     "Add one or more members to a sorted set, or update its score if it already exists",
@@ -937,6 +1028,16 @@ struct commandHelp {
     "Count the number of members in a sorted set between a given lexicographical range",
     4,
     "2.8.9" },
+    { "ZPOPMAX",
+    "key [count]",
+    "Remove and return members with the highest scores in a sorted set",
+    4,
+    "5.0.0" },
+    { "ZPOPMIN",
+    "key [count]",
+    "Remove and return members with the lowest scores in a sorted set",
+    4,
+    "5.0.0" },
     { "ZRANGE",
     "key start stop [WITHSCORES]",
     "Return a range of members in a sorted set, by index",

From 269e80526f1f90142661b9e25bff3a08639ce59c Mon Sep 17 00:00:00 2001
From: antirez <antirez@gmail.com>
Date: Fri, 8 Jun 2018 11:17:20 +0200
Subject: [PATCH 8/8] Implement DEBUG htstats-key.

---
 src/debug.c     | 29 +++++++++++++++++++++++++++++
 src/redis-cli.c |  2 ++
 2 files changed, 31 insertions(+)

diff --git a/src/debug.c b/src/debug.c
index 0ab864e7..078ac3c6 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -290,6 +290,7 @@ void debugCommand(client *c) {
 "crash-and-recover <milliseconds> -- Hard crash and restart after <milliseconds> delay.",
 "digest -- Outputs an hex signature representing the current DB content.",
 "htstats <dbid> -- Return hash table statistics of the specified Redis database.",
+"htstats-key <key> -- Like htstats but for the hash table stored as key's value.",
 "loadaof -- Flush the AOF buffers on disk and reload the AOF in memory.",
 "lua-always-replicate-commands (0|1) -- Setting it to 1 makes Lua replication defaulting to replicating single commands, without the script having to enable effects replication.",
 "object <key> -- Show low level info about key and associated value.",
@@ -547,6 +548,34 @@ NULL
         stats = sdscat(stats,buf);
 
         addReplyBulkSds(c,stats);
+    } else if (!strcasecmp(c->argv[1]->ptr,"htstats-key") && c->argc == 3) {
+        robj *o;
+        dict *ht = NULL;
+
+        if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nokeyerr))
+                == NULL) return;
+
+        /* Get the hash table reference from the object, if possible. */
+        switch (o->encoding) {
+        case OBJ_ENCODING_SKIPLIST:
+            {
+                zset *zs = o->ptr;
+                ht = zs->dict;
+            }
+            break;
+        case OBJ_ENCODING_HT:
+            ht = o->ptr;
+            break;
+        }
+
+        if (ht == NULL) {
+            addReplyError(c,"The value stored at the specified key is not "
+                            "represented using an hash table");
+        } else {
+            char buf[4096];
+            dictGetStats(buf,sizeof(buf),ht);
+            addReplyBulkCString(c,buf);
+        }
     } else if (!strcasecmp(c->argv[1]->ptr,"change-repl-id") && c->argc == 2) {
         serverLog(LL_WARNING,"Changing replication IDs after receiving DEBUG change-repl-id");
         changeReplicationId();
diff --git a/src/redis-cli.c b/src/redis-cli.c
index 0ee9f84e..af5e6a23 100644
--- a/src/redis-cli.c
+++ b/src/redis-cli.c
@@ -1075,6 +1075,8 @@ static int cliSendCommand(int argc, char **argv, long repeat) {
     if (!strcasecmp(command,"info") ||
         (argc >= 2 && !strcasecmp(command,"debug") &&
                        !strcasecmp(argv[1],"htstats")) ||
+        (argc >= 2 && !strcasecmp(command,"debug") &&
+                       !strcasecmp(argv[1],"htstats-key")) ||
         (argc >= 2 && !strcasecmp(command,"memory") &&
                       (!strcasecmp(argv[1],"malloc-stats") ||
                        !strcasecmp(argv[1],"doctor"))) ||