Wednesday, September 08, 2010

One thing I learned from Redis tests

Behind every useful product,
there is an awesome test suite.

Redis is an advanced key-value store. I have been following Redis development for quite sometime now. The speed of development is very pacy with numerous features and bug fixes being added almost every day. This is expected as it is a young database ( just over a year and a half ) trying to solve all the scalability problems for today's sophisticated web applications. Redis 2.0.0 was released recently with a lot of cool features.

As you can see, Redis 2.0.0 accommodates a lot of features and bug fixes. This is where Redis tests comes in. The Redis test suite is made of very well written bunch of TCL scripts consisting of functional test cases that are executed against a running Redis server instance. The test cases test everything between basic operations like SET and complex operations like SORT. The test suite is kept up-to-date as features and bug fixes are added. This enables drastic code refactoring possible that is critical to the survival of an opensource project.

Let's see how TCL interpreter interprets code. TCL interpreter expects every line of code to be of the form:

command arg1 arg2 ... argN

command can be a built-in TCL command, like set (wow, Redis commands seem to be inspired from TCL commands) or user-defined commands defined using the proc

proc commandName {arg1 arg2 ... argN} {body}

Arguments can be any of the TCL built-in types, (plain variable, lists, dictionary, array, and so on) procedures (passed by their names) or even raw TCL code (of the form "command arg1 arg2 ... argN"). Guess what the below snippet does:

% proc execTcl {code} {uplevel 1 $code}

% execTcl {set a 10}

You will see why that mini-TCL tutorial was needed in a moment.

The start_server TCL procedure is the centrepiece of the test suite. The procedure has the following declaration:

proc start_server {options {code undefined}}

Below is a list of important stuff it does in imperative order:

* Produce a configuration file taking settings from the options argument if any. start_server expects options to be a list of key-value pairs. Sets the tags for set of tests to be executed in the redis instance started by this start_server call. This enables you to selectively execute certain tests:

make test TAGS="list"

* Start the Redis server instance:

exec src/redis-server $config_file > $stdout 2> $stderr &

* Initialize a redis dictionary to identify the particular redis instance. Important is the key named client whose value is a procedure (returned from a call to redis) that is called everytime there is a need to send/receive redis commands. The redis procedure returns a handle to __dispatch__ that performs the network I/O. The handle is returned by a call to interp command:

interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id

* The code passed in as an argument is executed.

uplevel 1 $code

* code consists of a series of Redis test cases.

* After executing all the tests the redis instance is killed via a call to kill_server.

So far I have really enjoyed the time I have spent studying Redis. Redis has certainly become a very valuable tool to the developer community. Thanks to Salvatore, Peiter and all others involved in its development.

By the way, what was the one thing I learned from Redis tests??