

Reading and Writing Redis Protocol in Go - guywithabike
http://www.redisgreen.net/blog/2014/05/16/reading-and-writing_redis_protocol/

======
voidlogic
If any gophers need a solid redis library I would recommend:
[https://github.com/garyburd/redigo](https://github.com/garyburd/redigo)

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

~~~
guywithabike
(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.

~~~
eurleif
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?

~~~
BarkMore
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.

------
laurent123456
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?

~~~
pkulak
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?

~~~
Zikes
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.

------
nkozyra
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.

~~~
Zikes
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.

~~~
guywithabike
(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. :)

------
kristianp
I'm suprised at the number of companies using hosted Redis as reported on
[http://www.redisgreen.net/](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.

~~~
_bpo
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)

------
dolzenko
There is also a cool library for writing servers that follow RESP
[https://github.com/bsm/redeo](https://github.com/bsm/redeo)

------
microcolonel
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.

