Taking many of the ideas of Memcached, Redis is popular in-memory cache and datastructure store. Compared to Memcached, Redis provides a much richer set of operations including lists, sets, and sorted sets, although it does not provide Memcached's support for sharding keys across multiple machines (although Redis Cluster provides some of that functionality and is maturing).
In addition to its wide range of supported datastructures, one of the most compelling aspects of Redis' design is the simple protocol between the client and server, and this system will focus on implementing a simple version of the Redis server. (If you don't find this protocol particularly elegant, wait until we explore the Memcached protocol in a later system, and you may change your mind.)
As a quick aside, if you're looking for more examples of how Redis protocol works, the easiest way
to find them is to install Redis on your computer, run ngrep along the lines of
sudo ngrep -d l0 port 6379, and then use a redis client to send some commands!
That will give you a chance to see both the client and server protocol.
Given that there are a huge number of Redis commands, we're going to focus on implementing only a subset of the list commands, specifically: LLEN, LPUSH, LPOP and LRANGE.
Let's take a look at the input and output for each of those commands,
starting with LLEN which returns the length of a list, and whose
*2\r\n $4\r\n LLEN\r\n $7\r\n systems\r\n
LPUSH is used to prepend values to a list, and takes the format
LPUSH key value [value ...],
meaning it must specify at least one value to prepend, but may specify many. Note that the return value is
the new length of the list with new values added.
*3\r\n $5\r\n lpush\r\n $7\r\n systems\r\n $1\r\n a\r\n *4\r\n $5\r\n lpush\r\n $7\r\n systems\r\n $3\r\n bbb\r\n $2\r\n cc\r\n
LPOP is used to remove and return the first element in a list, in the form of
*2\r\n $4\r\n lpop\r\n $7\r\n systems\r\n
LRANGE retrieves all values in a list from the start to stop positions, inclusive.
The format is
LRANGE key start stop.
*4\r\n $6\r\n LRANGE\r\n $7\r\n systems\r\n $1\r\n 0\r\n $1 3\r\n
*3\r\n $1\r\n c\r\n $1\r\n b\r\n $1 a\r\n
Alright, now let's take a stab at combining all of those actions! Noting that we're using a slightly more compact representation here.
*2\r\n$4\r\nllen\r\n$6\r\npapers\r\n *4\r\n$6\r\nlrange\r\n$6\r\npapers\r\n$1\r\n0\r\n$3\r\n100\r\n *2\r\n$4\r\nlpop\r\n$6\r\npapers\r\n *3\r\n$5\r\nlpush\r\n$6\r\npapers\r\n$6\r\ndynamo\r\n *5\r\n$5\r\nlpush\r\n$6\r\npapers\r\n$4\r\nraft\r\n$5\r\npaxos\r\n$4\r\nswim\r\n *2\r\n$4\r\nlpop\r\n$6\r\npapers\r\n *2\r\n$4\r\nlpop\r\n$6\r\npapers\r\n *4\r\n$6\r\nlrange\r\n$6\r\npapers\r\n$1\r\n0\r\n$3\r\n100\r\n
:0\r\n *0\r\n $-1\r\n :1\r\n :4\r\n $4\r\nswim\r\n $5\r\npaxos\r\n *2\r\n$4\r\nraft\r\n$6\r\ndynamo\r\n
Once your implementations output works for that input, you're reading to take on the challenge below! As a couple of hints, note that LPUSH behaves in a very specific fashion when you specify multiple values.