GithubHelp home page GithubHelp logo

redigo's Introduction

Redigo

GoDoc

Redigo is a Go client for the Redis database.

Features

Documentation

Installation

Install Redigo using the "go get" command:

go get github.com/gomodule/redigo/redis

The Go distribution is Redigo's only dependency.

Related Projects

Contributing

See CONTRIBUTING.md.

License

Redigo is available under the Apache License, Version 2.0.

redigo's People

Contributors

178inaba avatar anacrolix avatar bits01 avatar cuonglm avatar dmitri-lerko avatar firstrow avatar fzambia avatar garyburd avatar imxyb avatar izumin5210 avatar mbyczkowski avatar mkabischev avatar pabigot avatar ramin0 avatar rhnvrm avatar shosti avatar stanhu avatar stevenh avatar terryh avatar theaj avatar thehippo avatar tomlinford avatar tortuoise avatar tp avatar vasayxtx avatar wenpeng avatar withshubh avatar wricardo avatar yjhmelody avatar zuoshuwen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redigo's Issues

SADD multiple

Hey,

Excuse me if this is a stupid question, but I'm brand new at golang.

The SADD method of redis can accept multiple values at once (an array) and add them all to the set. I'm struggling to work out how to do this with redigo syntax.

Does redigo support this functionality?

Thanks,

Documentation incorrect - Pool

There is an error in the (imho needlessly complex) redis.Pool Dial function.

The call to c.Do("AUTH", password) should return TWO arguments. Currently it just expects err.

Error handling requirements are not documented

The examples in the documentation frequently ignore error return values and there is no documentation indicating that the example code does so safely. It is unclear to me when I need to check for errors, so I started by reading the code for conn.Send(..) to see the conditions under which it will return an error.

What I found is that things like c.writeString() ignore the errors from c.writeN() and c.bw.Write() but return the error from the final c.bw.WriteString().

If these are correctly ignoring errors, I believe I can ignore the error return from all but my last conn.Send(...) call, and perhaps only read the error from Flush() or even the last Receive() executed, as the docs do.

The package would be much easier to use if errors didn't have to be checked after every call, but I don't feel I can do so unless the documentation is clear about when an error return value can be safely ignored.

Check for timeout before closing connection in conn.fatal

In many parts of conn (e.g conn.Receive, conn.Do) any error coming out of readReply will call c.fatal, which closes the connection.

If the error is a timeout, we probably don't want to close the network connection. You could check for a timeout if te, ok := e.(*net.OpError); ok && te.Timeout() {, and if so return the error directly instead of wrapping it in fatal.

Is this something you'd be prepared to include? If so I'll send a pull request for further discussion.

And thanks for redigo!

SLOWLOG convert to struct?

I am trying to convert the slowlog to a struct however I am having trouble using the API to do it. The struct I have is:
type Slowlog struct {
ID int64
Timestamp int64
Microseconds int64
Command []string
}

And the code to convert is:
var logs []Slowlog
err := redis.ScanStruct(reply, logs)

Error I am getting is: ScanStruct value must be non-nil pointer to a struct

However I can see the reply has benchmark data in it:
[[%!!(MISSING)s(int64=517) %!!(MISSING)s(int64=1402802827) %!!(MISSING)s(int64=22405) [MSET key:rand_int xx

Thanks in advance for any suggestions.

pool's conn error when redis server close the conn

When init the pool,run a long time. Then redis-server close the conn, while the pool's conn still keep in use. When the app get this colsed conn will get an error.

How to ensure that the closed conn pop from the pool?

redigo always random errors when many connections coming

I init redis pool in a model package, and one model(one Mysql table one model ) use RedisPool.Get() get a redis conn to DO redis command.

RedisPool = &redis.Pool{
        MaxIdle:     3,
        IdleTimeout: 240 * time.Second,
        TestOnBorrow: func(c redis.Conn, t time.Time) error {
            _, err := c.Do("PING")
            if err != nil {
                logs.Logger.Errorf("redis TestOnBorrow %v", err)
            }
            return err
        },
        Dial: func() (redis.Conn, error) {
            c, err := redis.Dial("tcp", server)
            if err != nil {
                logs.Logger.Errorf("Dail master redis server %s %v", server, err)
                return nil, err
            }
            //if password != "" {
            //  if _, err := c.Do("AUTH", password); err != nil {
            //      c.Close()
            //      return nil, err
            //  }
            //}
            logs.Logger.Debugf("Dail master redis server %s succeefully!", server)
            return c, err
        },
    }

When many connections coming redigo always random get errors like these:

[ERROR] [16:25:27] [NestPrepare @ class.go.29] GetName err: redigo: connection pool closed
[ERROR] [16:25:33] [NestPrepare @ class.go.29] GetName err: short write
[ERROR] [16:25:33] [NestPrepare @ class.go.29] GetName err: use of closed network connection
[ERROR] [16:25:33] [app.Rset error: redigo: unexpected type for Values, got type string
[ERROR] [16:25:33] [app.Rset error: redigo: unexpected type for Values, got type string

PubSub can use a pool connection but upon close, the pool discards the connection entirely instead of returning it back to the pool

Hey,

So i have tested some code where if i have a pool of say maxidle= 1, and I first do a pool.Get then conn.Do and then conn.Close, the pool.ActiveCount will report 1.

However, now if I do a Pool.Get and then conn.Subscribe and then conn.Unsubscribe and then conn.Close, the pool.ActiveCount will report 0.

Basically, if the connection was ever used as a PubSub conn, the pool will never accept it back and in-fact it will discard the connection when conn.Close is called.

Connection with user password

Is there a way to connect to a url with user and password?
Heroku suggests redigo to connect but redis to go url is user:pwd@host:port...

I can't figure out how to deal with that!

pool.Get doesn't return an err

The documentation seems to indicate that pool.Get will return an err,
// conn, err := pool.Get()
// defer conn.Close()
// // do something with the connection
but it doesn't seem to be doing that.. what's the best way to catch dial errors on pooled connections?

Read timeout and connection closing

Using Conn.DialTimeout with a nonzero readTimeout will return an error when using PubSubConn.Receive after the timeout, as expected.

Unfortunately, it will also close the connection when reporting the error which prevents me from using it this way.

package main

import (
    "fmt"
    "net"
    "time"

    "github.com/garyburd/redigo/redis"
)

func main() {
    conn, err := redis.DialTimeout("tcp", "localhost:6379", 0, time.Second, 0)
    if err != nil {
        panic(err)
    }
    pubsub := redis.PubSubConn{conn}
    go func() {
        for {
            fmt.Print(".") // to see the loop
            switch n := pubsub.Receive().(type) {
            case net.Error:
                if n.Timeout() {
                    fmt.Println("Timeout! Retrying")
                }
            case error:
                fmt.Println(n)
                return
            }
        }
    }()
    time.Sleep(time.Second * 2)
}

What I'd like is my goroutine to be not blocking on receive so I can end it, so I thought a timed out Receive in a loop could do the trick, but maybe there's an obvious solution, I'm pretty new to Go, so I would appreciate any pointer.

wrong number of arguments for 'hmset' command

Hi,
I tried to copy the struct example at http://godoc.org/github.com/garyburd/redigo/redis#example-Args but i kept getting the error, "ERR wrong number of arguments for 'hmset' command"

func main() {
c, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
    panic(err)
}

ss := &somestruct{}
ss.a = "apple"
ss.b = "banana"

if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(ss)...); err != nil {
    panic(err)
}

}
/*
/go/bin$ go run redis.go
panic: ERR wrong number of arguments for 'hmset' command

goroutine 1 [running]:
main.main()
/go/bin/redis.go:23 +0x389

goroutine 2 [syscall]:
exit status 2
*/

although, if i just enter my values in manually, it seems to work.

Consider blocking on request for connection from pool when exhausted

Presently, if a connection from a pool is requested with pool.Get() and the number of connections active has reached MaxActive then Get() will return the error errPoolClosed.

I feel its more expected by the application developer to always get a connection when calling pool.Get() -- unless the server is down or something. If a function needs to get/set data to redis, then it needs to do that, if the pool is exhausted because of high load on the application, then should it just keep looping asking for a connection until it's not exhausted anymore?

Perhaps having an async (as it is now) and sync Get() for requesting a connection.. like: pool.Get() to be synchronous and pool.GetOrFail() as async. Perhaps the sync Get() should also return an error after X time of waiting. pool.Get(&redis.Wait{5}) .. and a default config option on the pool struct.

I wonder how application developers are using redigo's Pool{} stuff, I can imagine most are leaving MaxActive as 0 and just hoping the max number of connections on their system or server never hits. Considering that, there should be a default for MaxActive, not infinity. And MaxIdle should be some reasonable number as well. I wonder how other database drivers do it...? ie. database/sql.

I think it keeps it much simpler and expected for the app developer. At least for me..

How to Process a ZREVRANGEBYSCORE .. WITHSCORES ?

I have the following that works, but seems a little silly?

values, _ := redis.Values(conn.Do("ZREVRANGEBYSCORE", "Ips:1min", "+inf", "-inf", "WITHSCORES", "LIMIT", "0", "10"))
pairs := make([]models.StringInt, len(values)/2)
y := 0
for i := 0; i < len(pairs); i = i + 1 {
v1, _ := values[y].([]byte)
v2, _ := values[y+1].([]byte)
sv2 := string(v2)
iv2, _ := strconv.Atoi(sv2)
pairs[i].S = string(v1)
pairs[i].I = iv2
y = y + 2
}

StringInt is just:

type StringInt struct {
S string;
I int;
}

I have a feeling I should maybe be using ScanStruct, but can't figure out the correct usage for a response like this?

The response is just something like:

redis 127.0.0.1:6379> ZREVRANGEBYSCORE Ips:5min +inf -inf WITHSCORES LIMIT 0 10

  1. "123.123.123.123"
  2. "10552"
  3. "12.12.12.12"
  4. "4360"
  5. "5.5.5.5"
  6. "3639"

Is redigo threadsafe?

I'm planning to use it for a webserver and there will many concurrent servings. For each I have to make a redis call. I'm calling the Dial just once and keeping the reference and using this reference for each call. However at my test it bads faily. What's the standart behaviour to work with concurrency? Should I open and close the connection for each redis call? Basically is it safe to use redigo concurrently?

Remove the fmt.Fprint fallback when encoding command arguments

Change argument encoding to encode simple values using strconv and return an error for other values.

  • The fmt.Fprint encoding is not useful for structured values like []int32.
  • There is more than one useful encoding for structured values (json, msgpack, gob). The application should pick which encoding is used.

How to add subscriptions while receiving messages?

In my project I have a goroutine running that basically waits for messages from a few redis pubsub channels:

for {
  switch v := r.Receive().(type) {
    // ...
  }
}

I also have another goroutine that's waiting on a go channel, and whenever it gets a value on that channel, it subscribes to that value. Like this:

select {
case newRedisChan := <-subscribe:
  r.Subscribe(newRedisChan) // this is the same r as in the last example, but we're in a different go routine
}

Needless to say, this provoked a data race (all hail the race detector) to show me the error of my ways. For reference:

WARNING: DATA RACE
Read by goroutine 27:
  github.com/garyburd/redigo/redis.(*pooledConnection).get()
      /home/vagrant/go/src/github.com/garyburd/redigo/redis/pool.go:262 +0x4c
  github.com/garyburd/redigo/redis.(*pooledConnection).Send()
      /home/vagrant/go/src/github.com/garyburd/redigo/redis/pool.go:338 +0x5a
  github.com/garyburd/redigo/redis.PubSubConn.Subscribe()
      /home/vagrant/go/src/github.com/garyburd/redigo/redis/pubsub.go:69 +0x9c
  bitbucket.org/aktau/ansible-go/cache.func·002()
      /home/vagrant/go/src/bitbucket.org/aktau/ansible-go/cache/redis.go:212 +0x385

Previous write by goroutine 23:
  github.com/garyburd/redigo/redis.(*pooledConnection).get()
      /home/vagrant/go/src/github.com/garyburd/redigo/redis/pool.go:263 +0xdc
  github.com/garyburd/redigo/redis.(*pooledConnection).Receive()
      /home/vagrant/go/src/github.com/garyburd/redigo/redis/pool.go:354 +0x61
  github.com/garyburd/redigo/redis.PubSubConn.Receive()
      /home/vagrant/go/src/github.com/garyburd/redigo/redis/pubsub.go:97 +0x76
  bitbucket.org/aktau/ansible-go/cache.(*RedisCache).autoInvalidate()
      /home/vagrant/go/src/bitbucket.org/aktau/ansible-go/cache/redis.go:228 +0x4cb

So, I'd like to ask what the idiomatic way to do this is? If Receive() returned a channel I guess I wouldn't even need to create 2 goroutines, as I could just put it all in a select statement. So I guess there's a good reason Receive() is a straight method call.

Also, is it safe to Close the connection from another goroutine? Because that's what I've been doing (I don't see how else I should do it).

EDIT: after looking at https://github.com/garyburd/redigo/blob/d39ee8868a981f93ca8cb17cfb1faf12efff49a6/redis/pubsub_test.go#L51-L89 (I admit, I should've done that first), I can see that you more or less follow the same style. I wonder why that doesn't race for you...

Encoding and decoding types

Redigo supports encoding an decoding of types known to the Redis server (boolean, float, integer, string). Redigo also encodes arbitrary types using fmt.Sprint. The fallback to fmt.Sprint is not useful and is an accident of the implementation.

Consider one of the following alternatives:

  • Encode and decode values using the Go 1.2 encoding interfaces (BinaryMarshaler, TextMarshaler, BinaryUnmarshaler, TextUnmarshaler). Issues: Should binary or text take precedence?
  • Encode and decode values with a marshaler interface specific to Redigo. The encoding/json and encoding/gob packages define their own interfaces.
  • Return an error for unsupported types. Applications should encode and decode values outside of Redigo.

Any change from the fmt.Sprint fallback can break existing uses of Redigo.

Some notes on time.Time:

  • Redis works with two time formats: seconds since the epoch encoded in decimal for the EXPIREAT, LASTSAVE, TIME commands and milliseconds since the epoch encoded in decimal for the PEXPIREAT command.
  • The encoding used by time.Time MarshalBinary and UnmarshalBinary is undocumented.
  • Times are currently encoded (via fmt.Sprint) with the format "2006-01-02 15:04:05.999999 -0700 MST". Nanoseconds since the epoch, milliseconds since the epoch and RFC3339 are all more useful encodings than the current encoding.

BRPOPLPUSH unexpected type for MultiBulk, got type []uint8

There seems to be some sort of protocol error when calling BRPOPLPUSH using client.Do. The following code SHOULD work but does not.

Tracing the code down to protocol level using strace shows a response getting received, just not properly handled as a MultiBulk reply (See python script).

Test code using the client: test/test_redis.go

package main
import (
"fmt"
"github.com/garyburd/redigo/redis"

)
func main() {
proto := "unix"
addr := "/var/run/redis.sock"

uu := "tmp"
Queue := "test"
// Payload := "junkjunk"
TmpQueue := fmt.Sprintf("%s_%s", Queue, uu)
client, e := redis.Dial(proto, addr);
if e != nil {
    fmt.Println("Connect", e)
}

fmt.Println("Queue", Queue)
fmt.Println("TmpQueue", TmpQueue)

resp, err := client.Do("BRPOPLPUSH", Queue, TmpQueue, "0")
if err != nil {
    fmt.Println("Do", err)
}


resp, err = redis.MultiBulk(resp, err)

resp, err = redis.MultiBulk(resp, err)
if err != nil {
    fmt.Println("MultiBulk", err)
}

_ = resp

}

Then in another terminal

redis /var/run/redis.sock> lpush test foobarbadbaz

The expected output should be the response (or not an error) but instead you see:

MultiBulk redigo: unexpected type for MultiBulk, got type []uint8

Test python script doing the protocol level command: test_mb.py

!/usr/bin/env python

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 6380))

buf = "*4\r\n$10\r\nBRPOPLPUSH\r\n$4\r\ntest\r\n$8\r\ntest_tmp\r\n$1\r\n0\r\n"
r = s.send(buf)
print 'client send', r, repr(buf)

r = s.recv(4096)
print 'server send', len(r), repr(r)

Running redis-server 2:2.4.14-0ubuntu1 from ubuntu lucid univers

ScanStruct not support sub struct

first,define two class
var table1, table2 struct {
Name string redis:"name"
Password string redis:"password"
Sub struct {
Addr string
Phone string
Age int
}
Sex int
}

then hmset table1 success

HGETALL also success

but last redis.ScanStruct(v, table2 )

error:Scan cannot convert from []uint8 to struct { Addr string; Phone string; Age int }

not support sub struct ? thanks

Add hash support

I couldn't get the Go + redigo equivalent of

HMSET myhash field1 "Hello" field2 "World"

to work ("ERR wrong number of arguments for 'hmset' command"). Native hash support would be nice to get around this and provide convenience functions.

The var ErrNil shoud be a type in reply.go

The var ErrNil shoud be a type in reply.go, so easy to judge it for ignore. On the other hand, I think the nil returned should not be considered a error.

The code:

literals, err := redis.String(conn.Do("HGET", "user", "root"))
if err != nil {
  log.Printf("Error (%s): %s\n", literals, err)
  return nil, err
}

Output:

 Error (): redigo: nil returned

Deadlock when trying to execute parallel Do commands

When I attempt to execute two requests in parallel, I end up in a deadlock.

I'm using Redis version 2.6.10 and I have a recent version of redigo 12c718e

Here is a short program to demonstrate the issue.

package main

import (
    "encoding/json"
    "fmt"
    "github.com/garyburd/redigo/redis"
)

type CRedis struct{ redis.Conn }

type Person struct {
    Name string
    Age  int
}

func (c *CRedis) Get(name string) (*Person, error) {
    p := Person{}
    jsonStr, err := redis.String(c.Do("HGET", "team", name))
    if err != nil && err != redis.ErrNil {
        fmt.Printf("Redis Fetch Error (%v)\n", err)
        return nil, err
    }
    err = json.Unmarshal([]byte(jsonStr), &p)
    if err != nil {
        fmt.Printf("Redis Fetch Error (JSON un marshall) (%v)\n", err)
        return nil, err
    }
    return &p, nil
}

func (c *CRedis) Set(p *Person) error {
    b, err := json.Marshal(p)
    if err != nil {
        fmt.Printf("Save JSON fail (%v): %s\n", p, err)
        return err
    }
    didSave, err := redis.Bool(c.Do("HSET", "team", p.Name, b))
    if err != nil && err != redis.ErrNil {
        fmt.Printf("Save Store Error for %v: %s\n", p, err)
        return err
    }
    if !didSave {
        return fmt.Errorf("Save failed for %v", p)
    }
    return nil
}

func main() {
    conn, err := redis.Dial("tcp", ":6379")
    s := CRedis{conn}
    if err != nil {
        fmt.Printf("Died connecting, %v", err)
        panic(1)
    }
    bob := Person{"Bob", 40}
    s.Set(&bob)
    c := make(chan bool)
    f := func(b chan bool) {
        p, err := s.Get("Bob")
        fmt.Printf("Response: %v, err: %s\n", p, err)
        b <- true
    }
    go f(c)
    go f(c)
    for i := 0; i < 2; i++ {
        <-c
    }
}

Output:

ew-mbp:src ewalker2$ ./red_lock 
==================
WARNING: DATA RACE
Write by goroutine 5:
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:117 +0x3b
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous write by goroutine 4:
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:117 +0x3b
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
==================
WARNING: DATA RACE
Write by goroutine 5:
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:118 +0x55
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous write by goroutine 4:
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:118 +0x55
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
==================
WARNING: DATA RACE
Write by goroutine 5:
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:121 +0x89
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous write by goroutine 4:
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:121 +0x89
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
==================
WARNING: DATA RACE
Write by goroutine 5:
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:128 +0x139
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous write by goroutine 4:
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:128 +0x139
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
==================
WARNING: DATA RACE
Read by goroutine 5:
  bufio.(*Writer).Write()
      /usr/local/go/src/pkg/bufio/bufio.go:492 +0x87
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:129 +0x1db
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous write by goroutine 4:
  bufio.(*Writer).Write()
      /usr/local/go/src/pkg/bufio/bufio.go:510 +0x430
  github.com/garyburd/redigo/redis.(*conn).writeLen()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:129 +0x1db
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:156 +0x72
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
==================
WARNING: DATA RACE
Write by goroutine 5:
  runtime.slicestringcopy()
      /usr/local/go/src/pkg/runtime/slice.c:280 +0x0
  bufio.(*Writer).WriteString()
      /usr/local/go/src/pkg/bufio/bufio.go:573 +0x2f8
  github.com/garyburd/redigo/redis.(*conn).writeString()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:135 +0x7a
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:164 +0x6fc
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous write by goroutine 4:
  runtime.slicestringcopy()
      /usr/local/go/src/pkg/runtime/slice.c:280 +0x0
  bufio.(*Writer).WriteString()
      /usr/local/go/src/pkg/bufio/bufio.go:573 +0x2f8
  github.com/garyburd/redigo/redis.(*conn).writeString()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:135 +0x7a
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:164 +0x6fc
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
==================
WARNING: DATA RACE
Write by goroutine 5:
  runtime.slicestringcopy()
      /usr/local/go/src/pkg/runtime/slice.c:280 +0x0
  bufio.(*Writer).WriteString()
      /usr/local/go/src/pkg/bufio/bufio.go:573 +0x2f8
  github.com/garyburd/redigo/redis.(*conn).writeString()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:136 +0xb0
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:164 +0x6fc
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous write by goroutine 4:
  runtime.slicestringcopy()
      /usr/local/go/src/pkg/runtime/slice.c:280 +0x0
  bufio.(*Writer).WriteString()
      /usr/local/go/src/pkg/bufio/bufio.go:573 +0x2f8
  github.com/garyburd/redigo/redis.(*conn).writeString()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:136 +0xb0
  github.com/garyburd/redigo/redis.(*conn).writeCommand()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:164 +0x6fc
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:370 +0x1a4
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
==================
WARNING: DATA RACE
Write by goroutine 4:
  bufio.(*Reader).fill()
      /usr/local/go/src/pkg/bufio/bufio.go:83 +0x2eb
  bufio.(*Reader).ReadSlice()
      /usr/local/go/src/pkg/bufio/bufio.go:262 +0x479
  github.com/garyburd/redigo/redis.(*conn).readLine()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:191 +0x5f
  github.com/garyburd/redigo/redis.(*conn).readReply()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:264 +0x43
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:402 +0x60d
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous read by goroutine 5:
  bufio.(*Reader).ReadSlice()
      /usr/local/go/src/pkg/bufio/bufio.go:247 +0xa7
  github.com/garyburd/redigo/redis.(*conn).readLine()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:191 +0x5f
  github.com/garyburd/redigo/redis.(*conn).readReply()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:264 +0x43
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:402 +0x60d
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
==================
WARNING: DATA RACE
Write by goroutine 4:
  bufio.(*Reader).ReadSlice()
      /usr/local/go/src/pkg/bufio/bufio.go:267 +0x595
  github.com/garyburd/redigo/redis.(*conn).readLine()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:191 +0x5f
  github.com/garyburd/redigo/redis.(*conn).readReply()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:264 +0x43
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:402 +0x60d
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Previous read by goroutine 5:
  bufio.(*Reader).ReadSlice()
      /usr/local/go/src/pkg/bufio/bufio.go:247 +0xc9
  github.com/garyburd/redigo/redis.(*conn).readLine()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:191 +0x5f
  github.com/garyburd/redigo/redis.(*conn).readReply()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:264 +0x43
  github.com/garyburd/redigo/redis.(*conn).Do()
      /Users/ewalker2/go/src/github.com/garyburd/redigo/redis/conn.go:402 +0x60d
  main.(*CRedis).Get()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:18 +0x1de
  main.func·001()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:59 +0x50
  gosched0()
      /usr/local/go/src/pkg/runtime/proc.c:1218 +0x9f

Goroutine 4 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:63 +0x2e9
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

Goroutine 5 (running) created at:
  main.main()
      /Users/ewalker2/Documents/soft/nav-go/src/red_lock.go:64 +0x304
  runtime.main()
      /usr/local/go/src/pkg/runtime/proc.c:182 +0x91

==================
Response: &{Bob 40}, err: %!s(<nil>)
Response: &{Bob 40}, err: %!s(<nil>)
Found 9 data race(s)
ew-mbp:src ewalker2$ 

test suite failing on Go 1.2

I forked the project since the test suite wasn't running for me, changed the test package name https://github.com/mattetti/redigo/commit/8d8bf3122d8830e0969651ddfff04553eed0c2b8 and run the test suite to see the following panic instead of error:

--- FAIL: TestBadScanStructArgs (0.00 seconds)
panic: reflect: NumField of non-struct type [recovered]
    panic: reflect: NumField of non-struct type

goroutine 26 [running]:
runtime.panic(0x1491e0, 0xc210086620)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/runtime/panic.c:266 +0xb6
testing.func·005()
    /usr/local/Cellar/go/1.2/libexec/src/pkg/testing/testing.go:385 +0xe8
runtime.panic(0x1491e0, 0xc210086620)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/runtime/panic.c:248 +0x106
reflect.(*rtype).NumField(0x148c40, 0x191f20)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/reflect/type.go:654 +0x6d
github.com/garyburd/redigo/redis.compileStructSpec(0x45eea8, 0x148c40, 0xc21008eb40, 0x0, 0x0, ...)
    /Users/mattetti/Code/golang/src/github.com/garyburd/redigo/redis/scan.go:230 +0x52
github.com/garyburd/redigo/redis.structSpecForType(0x45eea8, 0x148c40, 0x0)
    /Users/mattetti/Code/golang/src/github.com/garyburd/redigo/redis/scan.go:312 +0x1bd
github.com/garyburd/redigo/redis.ScanStruct(0xc21008b860, 0x2, 0x2, 0x139c20, 0xc210000f38, ...)
    /Users/mattetti/Code/golang/src/github.com/garyburd/redigo/redis/scan.go:332 +0x144
github.com/mattetti/redigo/redis.func·008(0x139c20, 0xc210000f38)
    /Users/mattetti/Code/golang/src/github.com/mattetti/redigo/redis/scan_test.go:190 +0x5f
github.com/mattetti/redigo/redis.TestBadScanStructArgs(0xc210090000)
    /Users/mattetti/Code/golang/src/github.com/mattetti/redigo/redis/scan_test.go:201 +0x15d
testing.tRunner(0xc210090000, 0x38cb08)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/testing/testing.go:391 +0x8b
created by testing.RunTests
    /usr/local/Cellar/go/1.2/libexec/src/pkg/testing/testing.go:471 +0x8b2

goroutine 1 [chan receive]:
testing.RunTests(0x20f910, 0x38c940, 0x17, 0x17, 0x1)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/testing/testing.go:472 +0x8d5
testing.Main(0x20f910, 0x38c940, 0x17, 0x17, 0x38f660, ...)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/testing/testing.go:403 +0x84
main.main()
    github.com/mattetti/redigo/redis/_test/_testmain.go:107 +0x9c

goroutine 12 [sleep]:
time.Sleep(0x3b9aca00)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/runtime/time.goc:31 +0x31
github.com/mattetti/redigo/redis.func·001()
    /Users/mattetti/Code/golang/src/github.com/mattetti/redigo/redis/conn_test.go:366 +0x2f
created by github.com/mattetti/redigo/redis.func·002
    /Users/mattetti/Code/golang/src/github.com/mattetti/redigo/redis/conn_test.go:369 +0xa0

goroutine 13 [sleep]:
time.Sleep(0x3b9aca00)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/runtime/time.goc:31 +0x31
github.com/mattetti/redigo/redis.func·001()
    /Users/mattetti/Code/golang/src/github.com/mattetti/redigo/redis/conn_test.go:366 +0x2f
created by github.com/mattetti/redigo/redis.func·002
    /Users/mattetti/Code/golang/src/github.com/mattetti/redigo/redis/conn_test.go:369 +0xa0
exit status 2
FAIL    github.com/mattetti/redigo/redis    0.025s

the interesting part:

runtime.panic(0x1491e0, 0xc210086620)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/runtime/panic.c:248 +0x106
reflect.(*rtype).NumField(0x148c40, 0x191f20)
    /usr/local/Cellar/go/1.2/libexec/src/pkg/reflect/type.go:654 +0x6d
github.com/garyburd/redigo/redis.compileStructSpec(0x45eea8, 0x148c40, 0xc21008eb40, 0x0, 0x0, ...)
    /Users/mattetti/Code/golang/src/github.com/garyburd/redigo/redis/scan.go:230 +0x52
github.com/garyburd/redigo/redis.structSpecForType(0x45eea8, 0x148c40, 0x0)
    /Users/mattetti/Code/golang/src/github.com/garyburd/redigo/redis/scan.go:312 +0x1bd
github.com/garyburd/redigo/redis.ScanStruct(0xc21008b860, 0x2, 0x2, 0x139c20, 0xc210000f38, ...)
    /Users/mattetti/Code/golang/src/github.com/garyburd/redigo/redis/scan.go:332 +0x144
github.com/mattetti/redigo/redis.func·008(0x139c20, 0xc210000f38)
    /Users/mattetti/Code/golang/src/github.com/mattetti/redigo/redis/scan_test.go:190 +0x5f
github.com/mattetti/redigo/redis.TestBadScanStructArgs(0xc210090000)
    /Users/mattetti/Code/golang/src/github.com/mattetti/redigo/redis/scan_test.go:201 +0x15d
    var v1 int
    test(&v1)

This is more than likely the desired expectation but since the test suite now fails, I figured I should report it.

Convenient methods for each redis commands ?

I wonder why there is no convenient methods for each redis command.

For instance instead of :

values, err := redis.Values(conn.Do("HGETALL", sym))
if err != nil {
  log.Fatal(err)
}
var stock Stock
if err := redis.ScanStruct(values, &stock); err != nil {
  log.Fatal(err)
}

It would be nice to have :

var stock Stock
if err := conn.Hgetall("key", &stock); err!= nil {
  log.Fatal(err)
}

With the following naive implementation :

func (c *Conn) Hgetall(key string, data interface{}) error {
values, err := redis.Values(conn.Do("HGETALL", key))
if err != nil {
  return err
}
if err := redis.ScanStruct(values, data); err != nil {
  return err
}
return nil
}

The implementation could be improved with the detection of data type and so invoking the corresponding Scan* function.

redis.Stringers helper returns an empty slice when using it on a Pipelined Do command

With the following example:

c.Send("MULTI")
c.Send("SET", "a", "a")
c.Send("SET", "b", "b")
r, err := c.Do("EXEC")
fmt.Println(r) // prints [OK, OK]

This works as expected per the documentation, but when the c.Do call is wrapped with the redis.Strings helper method, it returns an empty slice. Shouldn't it return a slice of all the responses?

c.Send("MULTI")
c.Send("SET", "a", "a")
c.Send("SET", "b", "b")
r, err := redis.Strings(c.Do("EXEC"))
fmt.Println(r) // prints []

Scan documentation typo

"The values pointed at by test must be a numeric type" should probably read "The values pointed at by dest must be a numeric type", i.e. s/test/dest/.

Weird default type handling using Fprint

Redigo handles complex types by "converting" them using Fprint which seems like a very odd thing to do.

default:
  var buf bytes.Buffer
  fmt.Fprint(&buf, arg)

Why use Fprint when there is encoding/gob? I don't really understand the design decision here.

gob.NewEncoder(&buf).Encode(&arg)

would be more robust.

Improve pool connection close

It is the application's responsibility to clear server state on a pooled connection before closing the connection. The state includes open transactions, pubsub, and pending replies.

The pool should recognize when there is state associated with the connection and either close the underlying connection or clear the state.

A sketch of the implementation is:

  • Add package-level var commandState map[string]struct { set, clear int }{ .... } where the keys are commands that modify server state and set/clear are bitmasks representing the state set or cleared by the command.
  • Add field state int to the pooled connection. Update state in Do and Send using commandState.
  • In the Close method, examine state bits and clear as appropriate.

time.Time

redigo could write time.Time to redis successfully:

if _, err = c.Do("SET","test:time",time.Now()); err!= nil{ panic(err)}

but it seems redigo could only get back the time as string. If there is a time.Time field in struct, is there any redigo function to scan this struct from redis?

Cant pass []string in Do("SINTER"...)

Sorry. Cant understand.

Why this works:

redis.Strings(redis_connection.Do("SINTER", "site.posts.index.tag=2", "site.posts.index.tag=10"))

but next - NOT (no result)

my_keys := make([]interface{}, 0)
my_keys = append(my_keys, "site.posts.index.tag=2")
my_keys = append(my_keys, "site.posts.index.tag=10")

redis.Strings(redis_connection.Do("SINTER", my_keys))

Clear pubsub state in pool close

Pubsub state can be cleared by:

  • Execute UNSUBSCRIBE and PUNSUBSCRIBE
  • Execute ECHO with a randomly generated argument.
  • Read responses until a response containing the ECHO argument is found.

Add pool connection limit.

Add option to specify maximum number of active connections for a pool. Return an error when the limit is reached.

Also, add methods to get number of active connections and high-water mark.

why redis return nil when I run test?

package main

import (
        "fmt"
        "net/http"
        "github.com/garyburd/redigo/redis"
        "log"
        "time"
)

var client redis.Conn

func hello(w http.ResponseWriter, r *http.Request) {
        r.ParseForm() 

        client.Do("INCR","hello")
        v,_ := client.Do("GET","hello")
        fmt.Printf("hello %s\n",v)
        fmt.Fprintf(w, "Hello! %s" ,v)
}

func main() {
        client, _ = redis.DialTimeout("tcp", "127.0.0.1:6379", 0, time.Millisecond, 0)
        defer client.Close()

        client.Do("SET","hello", 1)
        v,_ := client.Do("GET","hello")
        fmt.Printf("hello %s\n",v)

        http.HandleFunc("/", hello)
        err := http.ListenAndServe(":9090", nil)
        if err != nil {
            log.Fatal("ListenAndServe: ", err)
    }
}

when I run

wrk -t1 -c1 http://127.0.0.1:9090

and the log

...
hello 31
hello 32
hello %!s(<nil>)
hello %!s(<nil>)
hello %!s(<nil>)
...

"short write" error when application is under load

I've been benchmarking my application with wrk and it seems whenever I increase the load even a little bit, this function will fail

func authorizeKey(apiKey string, c redis.Conn) bool {
    status, err := redis.Bool(c.Do("SISMEMBER", "keys", apiKey))

    if err != nil {
        panic(err)
    }

    return status
}

with the following error

2013/03/18 21:14:46 http: panic serving [::1]:60858: short write
/usr/local/opt/go/src/pkg/net/http/server.go:589 (0x4750a)
        _func_004: buf.Write(debug.Stack())
/usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:1443 (0x12921)
        panic: reflect·call(d->fn, d->args, d->siz);
/Users/darth/projects/catsocket_go/main.go:46 (0x24b4)
        authorizeKey: panic(err)
/Users/darth/projects/catsocket_go/main.go:75 (0x2c8e)
        _func_002: if authorizeKey(apiKey, c) {
/usr/local/opt/go/src/pkg/net/http/server.go:703 (0x3b2fc)
        HandlerFunc.ServeHTTP: f(w, r)
/usr/local/opt/go/src/pkg/net/http/server.go:941 (0x3c150)
        (*ServeMux).ServeHTTP: mux.handler(r).ServeHTTP(w, r)
/usr/local/opt/go/src/pkg/net/http/server.go:669 (0x3b10f)
        (*conn).serve: handler.ServeHTTP(w, w.req)
/usr/local/Cellar/go/1.0.3/src/pkg/runtime/proc.c:271 (0x10a27)
        goexit: runtime·goexit(void)

Everything works fine when I'm just doing one request at a time, but when I do 10 at once I get this error.

Array of alternating values to a Struct/Map

There's, IMO, another type of "Multi Bulk Reply" you'd have to consider: Something a "SORT" would return.

Example:

SORT something GET otherkey:*->id GET otherkey:*->title

This would return alternating IDs and titles.

A call to a helper function for these alternations could be:

redis.AlternatingMultiBulkReply(src, &deststruct, "id", "title")

(The name really is horrible and way too long. I don't know what would be better)

Id and title would represent the corresponig struct tag content, you choose them in the order they appear in the result, which also sets the amount of returned values implicitly (and thus the offset needed for the next "row" of returned contents). If the length of src is not divisable by the amount of names that were given after the second argument, the function could either just not fill the remaining spaces or fail gracefully (or both).

I would also love if this worked for Maps. I would love if everything worked for maps!

Small issue with 'Pool' documentation/example

I think there is a small bug in the Pool documentation/example. On line 35 of pool.go, we have

    if err != nil {
        err = c.Do("AUTH", password)
    }

The 'err != nil' should be 'err == nil', no?

Thanks for the Redis client, BTW.

Support futures for blocking operations

I've run into a handful of cases now where I've wished I had blocking operations return over channels.

I'm quite positive this sketch below doesn't work yet for a variety of reasons, but I'd like your thoughts on whether or not this is something you would want to add to redigo. If yes, I'll play around with it and submit a PR; otherwise I'll just put it in a wrapper in my own projects.

type Future struct {
    Reply interface{}
    Err   error
}

func (c *conn) DoFuture(cmd string, args ...interface{}) chan *Future {
    ch := make(chan *Future)

    go func() {
        reply, err := c.Do(cmd, args...)
        ch <- &Future{reply, err}
    }()

    return ch
}

Problem when doing ScanStruct

just want to know if this lib be able to scan a struct like this:

type A struct{
    Prop1 string
    Prop2 []int32   
}

from the error message I got when doing ScanStruct, it seems slice of type other than byte are not supported; in the case of the struct above, it will raise an error because of the prop2 property which is not []byte type.

Would like to confirm this with you.

And thanks for the great work.. :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.