20 comments

[ 5.8 ms ] story [ 64.6 ms ] thread
Until not long ago there was a nice, growing attempt in Radix (redigo is basically the Redis library for Go now). It was barebones but did some of this.

Either way, I'm glad there's some work on another, although this looks like it's essentially a RESP parser rather than aiming to ever be a Redis interface.

This article seems to be more focused on teaching the reader about the Redis protocol and Go than an attempt to make a truly production-ready client interface. Certainly someone could take what they learn from this article and go on to do such a thing, but I think the author is more concerned with teaching than building.
(Author here) Correct -- this post is more about exploring RESP and why it's a neat protocol by implementing a very basic RESP reader. I wouldn't write a new Redis client unless I wanted to do something significantly different than the existing, battle-tested Redis clients like Redigo. This is the reason the code exists only in the blog post rather than in a GitHub repo that can be imported.

Besides, reading RESP objects is only the very first step in implementing a true Redis client. You need to handle response types (null bulk strings, null arrays, etc), opening TCP connections and handling errors therein, and offer up a sane API for end-users. And that's definitely outside the scope of the blog post. :)

(comment deleted)
If any gophers need a solid redis library I would recommend: https://github.com/garyburd/redigo

This is a mature library that I have been using for years.

+1 on this. I've used it in a previous project of mine. I did experiment with the other Redis-go libraries but Gary's felt the most complete and solid.
(Author, here.) I also recommend garyburd/redigo. I especially like the API. Instead of adding methods for every Redis command, redigo uses a single Do() method and then has several "type helper" methods that allow you to convert the response to various types simply by wrapping Do():

    value, err := redis.String(client.Do("GET", "mykey"))
Of course, this means you need to know about every command's response type and adds an extra (if small) level of verbosity, but in practice it fits very well with Go's philosophy of handling errors often and early. So, for instance, you're encouraged to handle the possibility that your key doesn't exist rather than barreling ahead with an empty string.
That looks really verbose. Also, am I correct in thinking that redis.Integer(client.Do("GET", "mykey")) would be an error, even if you've previously stored a int value in the key? If so, that seems like a trap.

How is this better for error handling? Why couldn't client.Get("mykey") signal an error in the same way, rather than returning an empty string?

The statement

    value, err := redis.Integer(client.Do("GET", "mykey"))
sets the int variable "value" to the decimal integer stored in "mykey". The variable err is set to a non-nil value if the value cannot be parsed as a decimal integer, the key is missing, the key is not a redis string, the connection is broken or any other error.

This method of error handling is convenient because the application only needs to check for one error. The alternative is to do something like:

    v, err := client.Do("GET", "mykey")
    if err != nil {
       // handle command error
    }
    p, ok := v.(string)
    if !ok {
        // handle error where p is not a redis string
    }
    i, err := strconv.ParseInt(string(p))
    if err != nil {
       // handle parse error
    }
There is no trap. If the connection to the server is healthy and the value stored for "mykey" is a decimal encoded integer, then redis.Integer(client.Do("GET", "mykey")) returns that integer.
Another +1 from me. I've been using it to do 1000s of requests/second in a project for over a year now. It's a little bit of work in that you have to explicitly get then return a connection to the pool, but the "defer" syntax makes that as painless as possible.
I wonder why "\r\n" and not just "\n" is the string separator in RESP. I get that it needs to be human readable but \n should be enough to make it readable in any text viewer. Or is there something more to it?
I was wondering the same thing. Perhaps "\r\n" is more friendly to Windows or Telnet users?
Probably only because CRLF is sort of the network line ending standard, used in many common protocols like http, imap, smtp, etc.
I wonder why errors and simple strings aren't just length-delimited as well. What do you gain by having both methods in one protocol?
I think human readability was something antirez really wanted, and having explicit line delimeters would make it easy to visually parse in a telnet session or building/debugging a client.
Oh great, an unflattering scrollbar you can't actually drag. This isn't as bad as native scroll hijacking, but why? There are lots of libraries which will handle making custom scrollbars for you if you don't feel like at least meeting the standard behaviour of scrollbars.
I'm suprised at the number of companies using hosted Redis as reported on http://www.redisgreen.net/ . I would have thought that ping times would affect performance of such a low level service? I have the same feeling about database hosting.
If the database provider hosts databases within the same data center that your app is in, ping times are no different than they would be if you ran your own DB in that data center.

Products like RedisGreen host their servers within Amazon and Google's data centers, and are meant for customers hosting their apps within those data centers. In the case of RedisGreen, you choose your hosting provider, region, etc, when provisioning resources.

If you sent database queries across the open Internet, ping latency is just one of several problems you'd encounter.

(disclosure: I work for RedisGreen)