gomodule / redigo Goto Github PK
View Code? Open in Web Editor NEWGo client for Redis
License: Apache License 2.0
Go client for Redis
License: Apache License 2.0
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!
I'm pretty new to redis, so this is might be very simple, but i've been having trouble getting any connection to redistogo working thus far.
Also, where would I insert the redistogo password into redigo?
Why is there an empty command Do() right before the connection is put back into the pool? @ pool.go Line 313
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$
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,
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?
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.
Pubsub state can be cleared by:
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.
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.
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.
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).
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
}
redis /var/run/redis.sock> lpush test foobarbadbaz
MultiBulk redigo: unexpected type for MultiBulk, got type []uint8
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)
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:
Any change from the fmt.Sprint fallback can break existing uses of Redigo.
Some notes on time.Time:
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.
"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/.
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!
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))
Using Go 1.1 on OS X, running go test
in "redis/" fails with:
./conn_test.go:73: undefined: redis.NewConnBufio
./conn_test.go:145: undefined: redis.NewConnBufio
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
- "123.123.123.123"
- "10552"
- "12.12.12.12"
- "4360"
- "5.5.5.5"
- "3639"
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...
The blocking commands (BLPOP, PRPOP, ...) have a timeout parameter.
Consider extending the connection specified timeout by the blocking command timeout or allowing the application to modify the connection read timeout.
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.
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!
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?
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.
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:
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.state int
to the pooled connection. Update state
in Do
and Send
using commandState
.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.
Anyone care to recommend this over the other Go Redis clients, or vice versa? Thanks :-).
Change argument encoding to encode simple values using strconv and return an error for other values.
I have a http benchmark for use redigo go redis client vs raw, Could you tell me why If I use redigo for redis operation, the throughput were sharply decreased.
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?
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>)
...
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
http://redis.io/topics/sentinel-clients
I have a rough plan in mind on how to implement sentinel support, but I have not done it yet because I don't need it.
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.
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..
So that if you are not interested in the channel pattern, you can use the same function to handle both messages.
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.. :)
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
}
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.
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
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
Implement Redis cluster features:
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?
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 []
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.
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.
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.