summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/garyburd
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-05-12 23:56:07 -0400
committerChristopher Speller <crspeller@gmail.com>2016-05-12 23:56:07 -0400
commit38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8 (patch)
treea4fde09672192b97d453ad605b030bd5a10c5a45 /vendor/github.com/garyburd
parent84d2482ddbff9564c9ad75b2d30af66e3ddfd44d (diff)
downloadchat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.tar.gz
chat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.tar.bz2
chat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.zip
Moving to glide
Diffstat (limited to 'vendor/github.com/garyburd')
-rw-r--r--vendor/github.com/garyburd/redigo/.travis.yml16
-rw-r--r--vendor/github.com/garyburd/redigo/README.markdown50
-rw-r--r--vendor/github.com/garyburd/redigo/internal/commandinfo.go2
-rw-r--r--vendor/github.com/garyburd/redigo/internal/commandinfo_test.go27
-rw-r--r--vendor/github.com/garyburd/redigo/internal/redistest/testdb.go68
-rw-r--r--vendor/github.com/garyburd/redigo/redis/conn_test.go670
-rw-r--r--vendor/github.com/garyburd/redigo/redis/doc.go2
-rw-r--r--vendor/github.com/garyburd/redigo/redis/pool_test.go684
-rw-r--r--vendor/github.com/garyburd/redigo/redis/pubsub_test.go148
-rw-r--r--vendor/github.com/garyburd/redigo/redis/reply_test.go179
-rw-r--r--vendor/github.com/garyburd/redigo/redis/scan_test.go440
-rw-r--r--vendor/github.com/garyburd/redigo/redis/script_test.go100
-rw-r--r--vendor/github.com/garyburd/redigo/redis/test_test.go177
-rw-r--r--vendor/github.com/garyburd/redigo/redis/zpop_example_test.go113
-rw-r--r--vendor/github.com/garyburd/redigo/redisx/connmux.go152
-rw-r--r--vendor/github.com/garyburd/redigo/redisx/connmux_test.go259
-rw-r--r--vendor/github.com/garyburd/redigo/redisx/doc.go17
17 files changed, 3102 insertions, 2 deletions
diff --git a/vendor/github.com/garyburd/redigo/.travis.yml b/vendor/github.com/garyburd/redigo/.travis.yml
new file mode 100644
index 000000000..80c179fe5
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/.travis.yml
@@ -0,0 +1,16 @@
+language: go
+sudo: false
+services:
+ - redis-server
+
+go:
+ - 1.4
+ - 1.5
+ - 1.6
+ - tip
+
+script:
+ - go get -t -v ./...
+ - diff -u <(echo -n) <(gofmt -d .)
+ - go vet $(go list ./... | grep -v /vendor/)
+ - go test -v -race ./...
diff --git a/vendor/github.com/garyburd/redigo/README.markdown b/vendor/github.com/garyburd/redigo/README.markdown
new file mode 100644
index 000000000..662690b3d
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/README.markdown
@@ -0,0 +1,50 @@
+Redigo
+======
+
+[![Build Status](https://travis-ci.org/garyburd/redigo.svg?branch=master)](https://travis-ci.org/garyburd/redigo)
+[![GoDoc](https://godoc.org/github.com/garyburd/redigo/redis?status.svg)](https://godoc.org/github.com/garyburd/redigo/redis)
+
+Redigo is a [Go](http://golang.org/) client for the [Redis](http://redis.io/) database.
+
+Features
+-------
+
+* A [Print-like](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Executing_Commands) API with support for all Redis commands.
+* [Pipelining](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining), including pipelined transactions.
+* [Publish/Subscribe](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Publish_and_Subscribe).
+* [Connection pooling](http://godoc.org/github.com/garyburd/redigo/redis#Pool).
+* [Script helper type](http://godoc.org/github.com/garyburd/redigo/redis#Script) with optimistic use of EVALSHA.
+* [Helper functions](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Reply_Helpers) for working with command replies.
+
+Documentation
+-------------
+
+- [API Reference](http://godoc.org/github.com/garyburd/redigo/redis)
+- [FAQ](https://github.com/garyburd/redigo/wiki/FAQ)
+
+Installation
+------------
+
+Install Redigo using the "go get" command:
+
+ go get github.com/garyburd/redigo/redis
+
+The Go distribution is Redigo's only dependency.
+
+Related Projects
+----------------
+
+- [rafaeljusto/redigomock](https://godoc.org/github.com/rafaeljusto/redigomock) - A mock library for Redigo.
+- [chasex/redis-go-cluster](https://github.com/chasex/redis-go-cluster) - A Redis cluster client implementation.
+- [FZambia/go-sentinel](https://github.com/FZambia/go-sentinel) - Redis Sentinel support for Redigo
+- [PuerkitoBio/redisc](https://github.com/PuerkitoBio/redisc) - Redis Cluster client built on top of Redigo
+
+Contributing
+------------
+
+Send email to Gary Burd (address in GitHub profile) before doing any work on Redigo.
+
+License
+-------
+
+Redigo is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
diff --git a/vendor/github.com/garyburd/redigo/internal/commandinfo.go b/vendor/github.com/garyburd/redigo/internal/commandinfo.go
index dbc60fc8e..11e584257 100644
--- a/vendor/github.com/garyburd/redigo/internal/commandinfo.go
+++ b/vendor/github.com/garyburd/redigo/internal/commandinfo.go
@@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations
// under the License.
-package internal
+package internal // import "github.com/garyburd/redigo/internal"
import (
"strings"
diff --git a/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go b/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go
new file mode 100644
index 000000000..118e94b67
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/internal/commandinfo_test.go
@@ -0,0 +1,27 @@
+package internal
+
+import "testing"
+
+func TestLookupCommandInfo(t *testing.T) {
+ for _, n := range []string{"watch", "WATCH", "wAtch"} {
+ if LookupCommandInfo(n) == (CommandInfo{}) {
+ t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n)
+ }
+ }
+}
+
+func benchmarkLookupCommandInfo(b *testing.B, names ...string) {
+ for i := 0; i < b.N; i++ {
+ for _, c := range names {
+ LookupCommandInfo(c)
+ }
+ }
+}
+
+func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) {
+ benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR")
+}
+
+func BenchmarkLookupCommandInfoMixedCase(b *testing.B) {
+ benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR")
+}
diff --git a/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go b/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go
new file mode 100644
index 000000000..b6f205b7f
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/internal/redistest/testdb.go
@@ -0,0 +1,68 @@
+// Copyright 2014 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+// Package redistest contains utilities for writing Redigo tests.
+package redistest
+
+import (
+ "errors"
+ "time"
+
+ "github.com/garyburd/redigo/redis"
+)
+
+type testConn struct {
+ redis.Conn
+}
+
+func (t testConn) Close() error {
+ _, err := t.Conn.Do("SELECT", "9")
+ if err != nil {
+ return nil
+ }
+ _, err = t.Conn.Do("FLUSHDB")
+ if err != nil {
+ return err
+ }
+ return t.Conn.Close()
+}
+
+// Dial dials the local Redis server and selects database 9. To prevent
+// stomping on real data, DialTestDB fails if database 9 contains data. The
+// returned connection flushes database 9 on close.
+func Dial() (redis.Conn, error) {
+ c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second)
+ if err != nil {
+ return nil, err
+ }
+
+ _, err = c.Do("SELECT", "9")
+ if err != nil {
+ c.Close()
+ return nil, err
+ }
+
+ n, err := redis.Int(c.Do("DBSIZE"))
+ if err != nil {
+ c.Close()
+ return nil, err
+ }
+
+ if n != 0 {
+ c.Close()
+ return nil, errors.New("database #9 is not empty, test can not continue")
+ }
+
+ return testConn{c}, nil
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/conn_test.go b/vendor/github.com/garyburd/redigo/redis/conn_test.go
new file mode 100644
index 000000000..2ead63326
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/conn_test.go
@@ -0,0 +1,670 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis_test
+
+import (
+ "bytes"
+ "io"
+ "math"
+ "net"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/garyburd/redigo/redis"
+)
+
+type testConn struct {
+ io.Reader
+ io.Writer
+}
+
+func (*testConn) Close() error { return nil }
+func (*testConn) LocalAddr() net.Addr { return nil }
+func (*testConn) RemoteAddr() net.Addr { return nil }
+func (*testConn) SetDeadline(t time.Time) error { return nil }
+func (*testConn) SetReadDeadline(t time.Time) error { return nil }
+func (*testConn) SetWriteDeadline(t time.Time) error { return nil }
+
+func dialTestConn(r io.Reader, w io.Writer) redis.DialOption {
+ return redis.DialNetDial(func(net, addr string) (net.Conn, error) {
+ return &testConn{Reader: r, Writer: w}, nil
+ })
+}
+
+var writeTests = []struct {
+ args []interface{}
+ expected string
+}{
+ {
+ []interface{}{"SET", "key", "value"},
+ "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
+ },
+ {
+ []interface{}{"SET", "key", "value"},
+ "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
+ },
+ {
+ []interface{}{"SET", "key", byte(100)},
+ "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
+ },
+ {
+ []interface{}{"SET", "key", 100},
+ "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
+ },
+ {
+ []interface{}{"SET", "key", int64(math.MinInt64)},
+ "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$20\r\n-9223372036854775808\r\n",
+ },
+ {
+ []interface{}{"SET", "key", float64(1349673917.939762)},
+ "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$21\r\n1.349673917939762e+09\r\n",
+ },
+ {
+ []interface{}{"SET", "key", ""},
+ "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
+ },
+ {
+ []interface{}{"SET", "key", nil},
+ "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
+ },
+ {
+ []interface{}{"ECHO", true, false},
+ "*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n",
+ },
+}
+
+func TestWrite(t *testing.T) {
+ for _, tt := range writeTests {
+ var buf bytes.Buffer
+ c, _ := redis.Dial("", "", dialTestConn(nil, &buf))
+ err := c.Send(tt.args[0].(string), tt.args[1:]...)
+ if err != nil {
+ t.Errorf("Send(%v) returned error %v", tt.args, err)
+ continue
+ }
+ c.Flush()
+ actual := buf.String()
+ if actual != tt.expected {
+ t.Errorf("Send(%v) = %q, want %q", tt.args, actual, tt.expected)
+ }
+ }
+}
+
+var errorSentinel = &struct{}{}
+
+var readTests = []struct {
+ reply string
+ expected interface{}
+}{
+ {
+ "+OK\r\n",
+ "OK",
+ },
+ {
+ "+PONG\r\n",
+ "PONG",
+ },
+ {
+ "@OK\r\n",
+ errorSentinel,
+ },
+ {
+ "$6\r\nfoobar\r\n",
+ []byte("foobar"),
+ },
+ {
+ "$-1\r\n",
+ nil,
+ },
+ {
+ ":1\r\n",
+ int64(1),
+ },
+ {
+ ":-2\r\n",
+ int64(-2),
+ },
+ {
+ "*0\r\n",
+ []interface{}{},
+ },
+ {
+ "*-1\r\n",
+ nil,
+ },
+ {
+ "*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n",
+ []interface{}{[]byte("foo"), []byte("bar"), []byte("Hello"), []byte("World")},
+ },
+ {
+ "*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n",
+ []interface{}{[]byte("foo"), nil, []byte("bar")},
+ },
+
+ {
+ // "x" is not a valid length
+ "$x\r\nfoobar\r\n",
+ errorSentinel,
+ },
+ {
+ // -2 is not a valid length
+ "$-2\r\n",
+ errorSentinel,
+ },
+ {
+ // "x" is not a valid integer
+ ":x\r\n",
+ errorSentinel,
+ },
+ {
+ // missing \r\n following value
+ "$6\r\nfoobar",
+ errorSentinel,
+ },
+ {
+ // short value
+ "$6\r\nxx",
+ errorSentinel,
+ },
+ {
+ // long value
+ "$6\r\nfoobarx\r\n",
+ errorSentinel,
+ },
+}
+
+func TestRead(t *testing.T) {
+ for _, tt := range readTests {
+ c, _ := redis.Dial("", "", dialTestConn(strings.NewReader(tt.reply), nil))
+ actual, err := c.Receive()
+ if tt.expected == errorSentinel {
+ if err == nil {
+ t.Errorf("Receive(%q) did not return expected error", tt.reply)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("Receive(%q) returned error %v", tt.reply, err)
+ continue
+ }
+ if !reflect.DeepEqual(actual, tt.expected) {
+ t.Errorf("Receive(%q) = %v, want %v", tt.reply, actual, tt.expected)
+ }
+ }
+ }
+}
+
+var testCommands = []struct {
+ args []interface{}
+ expected interface{}
+}{
+ {
+ []interface{}{"PING"},
+ "PONG",
+ },
+ {
+ []interface{}{"SET", "foo", "bar"},
+ "OK",
+ },
+ {
+ []interface{}{"GET", "foo"},
+ []byte("bar"),
+ },
+ {
+ []interface{}{"GET", "nokey"},
+ nil,
+ },
+ {
+ []interface{}{"MGET", "nokey", "foo"},
+ []interface{}{nil, []byte("bar")},
+ },
+ {
+ []interface{}{"INCR", "mycounter"},
+ int64(1),
+ },
+ {
+ []interface{}{"LPUSH", "mylist", "foo"},
+ int64(1),
+ },
+ {
+ []interface{}{"LPUSH", "mylist", "bar"},
+ int64(2),
+ },
+ {
+ []interface{}{"LRANGE", "mylist", 0, -1},
+ []interface{}{[]byte("bar"), []byte("foo")},
+ },
+ {
+ []interface{}{"MULTI"},
+ "OK",
+ },
+ {
+ []interface{}{"LRANGE", "mylist", 0, -1},
+ "QUEUED",
+ },
+ {
+ []interface{}{"PING"},
+ "QUEUED",
+ },
+ {
+ []interface{}{"EXEC"},
+ []interface{}{
+ []interface{}{[]byte("bar"), []byte("foo")},
+ "PONG",
+ },
+ },
+}
+
+func TestDoCommands(t *testing.T) {
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+
+ for _, cmd := range testCommands {
+ actual, err := c.Do(cmd.args[0].(string), cmd.args[1:]...)
+ if err != nil {
+ t.Errorf("Do(%v) returned error %v", cmd.args, err)
+ continue
+ }
+ if !reflect.DeepEqual(actual, cmd.expected) {
+ t.Errorf("Do(%v) = %v, want %v", cmd.args, actual, cmd.expected)
+ }
+ }
+}
+
+func TestPipelineCommands(t *testing.T) {
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+
+ for _, cmd := range testCommands {
+ if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
+ t.Fatalf("Send(%v) returned error %v", cmd.args, err)
+ }
+ }
+ if err := c.Flush(); err != nil {
+ t.Errorf("Flush() returned error %v", err)
+ }
+ for _, cmd := range testCommands {
+ actual, err := c.Receive()
+ if err != nil {
+ t.Fatalf("Receive(%v) returned error %v", cmd.args, err)
+ }
+ if !reflect.DeepEqual(actual, cmd.expected) {
+ t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
+ }
+ }
+}
+
+func TestBlankCommmand(t *testing.T) {
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+
+ for _, cmd := range testCommands {
+ if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
+ t.Fatalf("Send(%v) returned error %v", cmd.args, err)
+ }
+ }
+ reply, err := redis.Values(c.Do(""))
+ if err != nil {
+ t.Fatalf("Do() returned error %v", err)
+ }
+ if len(reply) != len(testCommands) {
+ t.Fatalf("len(reply)=%d, want %d", len(reply), len(testCommands))
+ }
+ for i, cmd := range testCommands {
+ actual := reply[i]
+ if !reflect.DeepEqual(actual, cmd.expected) {
+ t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
+ }
+ }
+}
+
+func TestRecvBeforeSend(t *testing.T) {
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+ done := make(chan struct{})
+ go func() {
+ c.Receive()
+ close(done)
+ }()
+ time.Sleep(time.Millisecond)
+ c.Send("PING")
+ c.Flush()
+ <-done
+ _, err = c.Do("")
+ if err != nil {
+ t.Fatalf("error=%v", err)
+ }
+}
+
+func TestError(t *testing.T) {
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+
+ c.Do("SET", "key", "val")
+ _, err = c.Do("HSET", "key", "fld", "val")
+ if err == nil {
+ t.Errorf("Expected err for HSET on string key.")
+ }
+ if c.Err() != nil {
+ t.Errorf("Conn has Err()=%v, expect nil", c.Err())
+ }
+ _, err = c.Do("SET", "key", "val")
+ if err != nil {
+ t.Errorf("Do(SET, key, val) returned error %v, expected nil.", err)
+ }
+}
+
+func TestReadTimeout(t *testing.T) {
+ l, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("net.Listen returned %v", err)
+ }
+ defer l.Close()
+
+ go func() {
+ for {
+ c, err := l.Accept()
+ if err != nil {
+ return
+ }
+ go func() {
+ time.Sleep(time.Second)
+ c.Write([]byte("+OK\r\n"))
+ c.Close()
+ }()
+ }
+ }()
+
+ // Do
+
+ c1, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
+ if err != nil {
+ t.Fatalf("redis.Dial returned %v", err)
+ }
+ defer c1.Close()
+
+ _, err = c1.Do("PING")
+ if err == nil {
+ t.Fatalf("c1.Do() returned nil, expect error")
+ }
+ if c1.Err() == nil {
+ t.Fatalf("c1.Err() = nil, expect error")
+ }
+
+ // Send/Flush/Receive
+
+ c2, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
+ if err != nil {
+ t.Fatalf("redis.Dial returned %v", err)
+ }
+ defer c2.Close()
+
+ c2.Send("PING")
+ c2.Flush()
+ _, err = c2.Receive()
+ if err == nil {
+ t.Fatalf("c2.Receive() returned nil, expect error")
+ }
+ if c2.Err() == nil {
+ t.Fatalf("c2.Err() = nil, expect error")
+ }
+}
+
+var dialErrors = []struct {
+ rawurl string
+ expectedError string
+}{
+ {
+ "localhost",
+ "invalid redis URL scheme",
+ },
+ // The error message for invalid hosts is diffferent in different
+ // versions of Go, so just check that there is an error message.
+ {
+ "redis://weird url",
+ "",
+ },
+ {
+ "redis://foo:bar:baz",
+ "",
+ },
+ {
+ "http://www.google.com",
+ "invalid redis URL scheme: http",
+ },
+ {
+ "redis://localhost:6379/abc123",
+ "invalid database: abc123",
+ },
+}
+
+func TestDialURLErrors(t *testing.T) {
+ for _, d := range dialErrors {
+ _, err := redis.DialURL(d.rawurl)
+ if err == nil || !strings.Contains(err.Error(), d.expectedError) {
+ t.Errorf("DialURL did not return expected error (expected %v to contain %s)", err, d.expectedError)
+ }
+ }
+}
+
+func TestDialURLPort(t *testing.T) {
+ checkPort := func(network, address string) (net.Conn, error) {
+ if address != "localhost:6379" {
+ t.Errorf("DialURL did not set port to 6379 by default (got %v)", address)
+ }
+ return nil, nil
+ }
+ _, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort))
+ if err != nil {
+ t.Error("dial error:", err)
+ }
+}
+
+func TestDialURLHost(t *testing.T) {
+ checkHost := func(network, address string) (net.Conn, error) {
+ if address != "localhost:6379" {
+ t.Errorf("DialURL did not set host to localhost by default (got %v)", address)
+ }
+ return nil, nil
+ }
+ _, err := redis.DialURL("redis://:6379", redis.DialNetDial(checkHost))
+ if err != nil {
+ t.Error("dial error:", err)
+ }
+}
+
+func TestDialURLPassword(t *testing.T) {
+ var buf bytes.Buffer
+ _, err := redis.DialURL("redis://x:abc123@localhost", dialTestConn(strings.NewReader("+OK\r\n"), &buf))
+ if err != nil {
+ t.Error("dial error:", err)
+ }
+ expected := "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"
+ actual := buf.String()
+ if actual != expected {
+ t.Errorf("commands = %q, want %q", actual, expected)
+ }
+}
+
+func TestDialURLDatabase(t *testing.T) {
+ var buf3 bytes.Buffer
+ _, err3 := redis.DialURL("redis://localhost/3", dialTestConn(strings.NewReader("+OK\r\n"), &buf3))
+ if err3 != nil {
+ t.Error("dial error:", err3)
+ }
+ expected3 := "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"
+ actual3 := buf3.String()
+ if actual3 != expected3 {
+ t.Errorf("commands = %q, want %q", actual3, expected3)
+ }
+ // empty DB means 0
+ var buf0 bytes.Buffer
+ _, err0 := redis.DialURL("redis://localhost/", dialTestConn(strings.NewReader("+OK\r\n"), &buf0))
+ if err0 != nil {
+ t.Error("dial error:", err0)
+ }
+ expected0 := ""
+ actual0 := buf0.String()
+ if actual0 != expected0 {
+ t.Errorf("commands = %q, want %q", actual0, expected0)
+ }
+}
+
+// Connect to local instance of Redis running on the default port.
+func ExampleDial() {
+ c, err := redis.Dial("tcp", ":6379")
+ if err != nil {
+ // handle error
+ }
+ defer c.Close()
+}
+
+// Connect to remote instance of Redis using a URL.
+func ExampleDialURL() {
+ c, err := redis.DialURL(os.Getenv("REDIS_URL"))
+ if err != nil {
+ // handle connection error
+ }
+ defer c.Close()
+}
+
+// TextExecError tests handling of errors in a transaction. See
+// http://redis.io/topics/transactions for information on how Redis handles
+// errors in a transaction.
+func TestExecError(t *testing.T) {
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+
+ // Execute commands that fail before EXEC is called.
+
+ c.Do("DEL", "k0")
+ c.Do("ZADD", "k0", 0, 0)
+ c.Send("MULTI")
+ c.Send("NOTACOMMAND", "k0", 0, 0)
+ c.Send("ZINCRBY", "k0", 0, 0)
+ v, err := c.Do("EXEC")
+ if err == nil {
+ t.Fatalf("EXEC returned values %v, expected error", v)
+ }
+
+ // Execute commands that fail after EXEC is called. The first command
+ // returns an error.
+
+ c.Do("DEL", "k1")
+ c.Do("ZADD", "k1", 0, 0)
+ c.Send("MULTI")
+ c.Send("HSET", "k1", 0, 0)
+ c.Send("ZINCRBY", "k1", 0, 0)
+ v, err = c.Do("EXEC")
+ if err != nil {
+ t.Fatalf("EXEC returned error %v", err)
+ }
+
+ vs, err := redis.Values(v, nil)
+ if err != nil {
+ t.Fatalf("Values(v) returned error %v", err)
+ }
+
+ if len(vs) != 2 {
+ t.Fatalf("len(vs) == %d, want 2", len(vs))
+ }
+
+ if _, ok := vs[0].(error); !ok {
+ t.Fatalf("first result is type %T, expected error", vs[0])
+ }
+
+ if _, ok := vs[1].([]byte); !ok {
+ t.Fatalf("second result is type %T, expected []byte", vs[1])
+ }
+
+ // Execute commands that fail after EXEC is called. The second command
+ // returns an error.
+
+ c.Do("ZADD", "k2", 0, 0)
+ c.Send("MULTI")
+ c.Send("ZINCRBY", "k2", 0, 0)
+ c.Send("HSET", "k2", 0, 0)
+ v, err = c.Do("EXEC")
+ if err != nil {
+ t.Fatalf("EXEC returned error %v", err)
+ }
+
+ vs, err = redis.Values(v, nil)
+ if err != nil {
+ t.Fatalf("Values(v) returned error %v", err)
+ }
+
+ if len(vs) != 2 {
+ t.Fatalf("len(vs) == %d, want 2", len(vs))
+ }
+
+ if _, ok := vs[0].([]byte); !ok {
+ t.Fatalf("first result is type %T, expected []byte", vs[0])
+ }
+
+ if _, ok := vs[1].(error); !ok {
+ t.Fatalf("second result is type %T, expected error", vs[2])
+ }
+}
+
+func BenchmarkDoEmpty(b *testing.B) {
+ b.StopTimer()
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer c.Close()
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ if _, err := c.Do(""); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkDoPing(b *testing.B) {
+ b.StopTimer()
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer c.Close()
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ if _, err := c.Do("PING"); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/doc.go b/vendor/github.com/garyburd/redigo/redis/doc.go
index 1ae6f0cc2..a5cd454af 100644
--- a/vendor/github.com/garyburd/redigo/redis/doc.go
+++ b/vendor/github.com/garyburd/redigo/redis/doc.go
@@ -166,4 +166,4 @@
// if _, err := redis.Scan(reply, &value1, &value2); err != nil {
// // handle error
// }
-package redis
+package redis // import "github.com/garyburd/redigo/redis"
diff --git a/vendor/github.com/garyburd/redigo/redis/pool_test.go b/vendor/github.com/garyburd/redigo/redis/pool_test.go
new file mode 100644
index 000000000..9419a128f
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/pool_test.go
@@ -0,0 +1,684 @@
+// Copyright 2011 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis_test
+
+import (
+ "errors"
+ "io"
+ "reflect"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/garyburd/redigo/redis"
+)
+
+type poolTestConn struct {
+ d *poolDialer
+ err error
+ redis.Conn
+}
+
+func (c *poolTestConn) Close() error {
+ c.d.mu.Lock()
+ c.d.open -= 1
+ c.d.mu.Unlock()
+ return c.Conn.Close()
+}
+
+func (c *poolTestConn) Err() error { return c.err }
+
+func (c *poolTestConn) Do(commandName string, args ...interface{}) (interface{}, error) {
+ if commandName == "ERR" {
+ c.err = args[0].(error)
+ commandName = "PING"
+ }
+ if commandName != "" {
+ c.d.commands = append(c.d.commands, commandName)
+ }
+ return c.Conn.Do(commandName, args...)
+}
+
+func (c *poolTestConn) Send(commandName string, args ...interface{}) error {
+ c.d.commands = append(c.d.commands, commandName)
+ return c.Conn.Send(commandName, args...)
+}
+
+type poolDialer struct {
+ mu sync.Mutex
+ t *testing.T
+ dialed int
+ open int
+ commands []string
+ dialErr error
+}
+
+func (d *poolDialer) dial() (redis.Conn, error) {
+ d.mu.Lock()
+ d.dialed += 1
+ dialErr := d.dialErr
+ d.mu.Unlock()
+ if dialErr != nil {
+ return nil, d.dialErr
+ }
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ return nil, err
+ }
+ d.mu.Lock()
+ d.open += 1
+ d.mu.Unlock()
+ return &poolTestConn{d: d, Conn: c}, nil
+}
+
+func (d *poolDialer) check(message string, p *redis.Pool, dialed, open int) {
+ d.mu.Lock()
+ if d.dialed != dialed {
+ d.t.Errorf("%s: dialed=%d, want %d", message, d.dialed, dialed)
+ }
+ if d.open != open {
+ d.t.Errorf("%s: open=%d, want %d", message, d.open, open)
+ }
+ if active := p.ActiveCount(); active != open {
+ d.t.Errorf("%s: active=%d, want %d", message, active, open)
+ }
+ d.mu.Unlock()
+}
+
+func TestPoolReuse(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ Dial: d.dial,
+ }
+
+ for i := 0; i < 10; i++ {
+ c1 := p.Get()
+ c1.Do("PING")
+ c2 := p.Get()
+ c2.Do("PING")
+ c1.Close()
+ c2.Close()
+ }
+
+ d.check("before close", p, 2, 2)
+ p.Close()
+ d.check("after close", p, 2, 0)
+}
+
+func TestPoolMaxIdle(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ Dial: d.dial,
+ }
+ defer p.Close()
+
+ for i := 0; i < 10; i++ {
+ c1 := p.Get()
+ c1.Do("PING")
+ c2 := p.Get()
+ c2.Do("PING")
+ c3 := p.Get()
+ c3.Do("PING")
+ c1.Close()
+ c2.Close()
+ c3.Close()
+ }
+ d.check("before close", p, 12, 2)
+ p.Close()
+ d.check("after close", p, 12, 0)
+}
+
+func TestPoolError(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ Dial: d.dial,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ c.Do("ERR", io.EOF)
+ if c.Err() == nil {
+ t.Errorf("expected c.Err() != nil")
+ }
+ c.Close()
+
+ c = p.Get()
+ c.Do("ERR", io.EOF)
+ c.Close()
+
+ d.check(".", p, 2, 0)
+}
+
+func TestPoolClose(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ Dial: d.dial,
+ }
+ defer p.Close()
+
+ c1 := p.Get()
+ c1.Do("PING")
+ c2 := p.Get()
+ c2.Do("PING")
+ c3 := p.Get()
+ c3.Do("PING")
+
+ c1.Close()
+ if _, err := c1.Do("PING"); err == nil {
+ t.Errorf("expected error after connection closed")
+ }
+
+ c2.Close()
+ c2.Close()
+
+ p.Close()
+
+ d.check("after pool close", p, 3, 1)
+
+ if _, err := c1.Do("PING"); err == nil {
+ t.Errorf("expected error after connection and pool closed")
+ }
+
+ c3.Close()
+
+ d.check("after conn close", p, 3, 0)
+
+ c1 = p.Get()
+ if _, err := c1.Do("PING"); err == nil {
+ t.Errorf("expected error after pool closed")
+ }
+}
+
+func TestPoolTimeout(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ IdleTimeout: 300 * time.Second,
+ Dial: d.dial,
+ }
+ defer p.Close()
+
+ now := time.Now()
+ redis.SetNowFunc(func() time.Time { return now })
+ defer redis.SetNowFunc(time.Now)
+
+ c := p.Get()
+ c.Do("PING")
+ c.Close()
+
+ d.check("1", p, 1, 1)
+
+ now = now.Add(p.IdleTimeout)
+
+ c = p.Get()
+ c.Do("PING")
+ c.Close()
+
+ d.check("2", p, 2, 1)
+}
+
+func TestPoolConcurrenSendReceive(t *testing.T) {
+ p := &redis.Pool{
+ Dial: redis.DialDefaultServer,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ done := make(chan error, 1)
+ go func() {
+ _, err := c.Receive()
+ done <- err
+ }()
+ c.Send("PING")
+ c.Flush()
+ err := <-done
+ if err != nil {
+ t.Fatalf("Receive() returned error %v", err)
+ }
+ _, err = c.Do("")
+ if err != nil {
+ t.Fatalf("Do() returned error %v", err)
+ }
+ c.Close()
+}
+
+func TestPoolBorrowCheck(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ Dial: d.dial,
+ TestOnBorrow: func(redis.Conn, time.Time) error { return redis.Error("BLAH") },
+ }
+ defer p.Close()
+
+ for i := 0; i < 10; i++ {
+ c := p.Get()
+ c.Do("PING")
+ c.Close()
+ }
+ d.check("1", p, 10, 1)
+}
+
+func TestPoolMaxActive(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ MaxActive: 2,
+ Dial: d.dial,
+ }
+ defer p.Close()
+
+ c1 := p.Get()
+ c1.Do("PING")
+ c2 := p.Get()
+ c2.Do("PING")
+
+ d.check("1", p, 2, 2)
+
+ c3 := p.Get()
+ if _, err := c3.Do("PING"); err != redis.ErrPoolExhausted {
+ t.Errorf("expected pool exhausted")
+ }
+
+ c3.Close()
+ d.check("2", p, 2, 2)
+ c2.Close()
+ d.check("3", p, 2, 2)
+
+ c3 = p.Get()
+ if _, err := c3.Do("PING"); err != nil {
+ t.Errorf("expected good channel, err=%v", err)
+ }
+ c3.Close()
+
+ d.check("4", p, 2, 2)
+}
+
+func TestPoolMonitorCleanup(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ MaxActive: 2,
+ Dial: d.dial,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ c.Send("MONITOR")
+ c.Close()
+
+ d.check("", p, 1, 0)
+}
+
+func TestPoolPubSubCleanup(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ MaxActive: 2,
+ Dial: d.dial,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ c.Send("SUBSCRIBE", "x")
+ c.Close()
+
+ want := []string{"SUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"}
+ if !reflect.DeepEqual(d.commands, want) {
+ t.Errorf("got commands %v, want %v", d.commands, want)
+ }
+ d.commands = nil
+
+ c = p.Get()
+ c.Send("PSUBSCRIBE", "x*")
+ c.Close()
+
+ want = []string{"PSUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"}
+ if !reflect.DeepEqual(d.commands, want) {
+ t.Errorf("got commands %v, want %v", d.commands, want)
+ }
+ d.commands = nil
+}
+
+func TestPoolTransactionCleanup(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 2,
+ MaxActive: 2,
+ Dial: d.dial,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ c.Do("WATCH", "key")
+ c.Do("PING")
+ c.Close()
+
+ want := []string{"WATCH", "PING", "UNWATCH"}
+ if !reflect.DeepEqual(d.commands, want) {
+ t.Errorf("got commands %v, want %v", d.commands, want)
+ }
+ d.commands = nil
+
+ c = p.Get()
+ c.Do("WATCH", "key")
+ c.Do("UNWATCH")
+ c.Do("PING")
+ c.Close()
+
+ want = []string{"WATCH", "UNWATCH", "PING"}
+ if !reflect.DeepEqual(d.commands, want) {
+ t.Errorf("got commands %v, want %v", d.commands, want)
+ }
+ d.commands = nil
+
+ c = p.Get()
+ c.Do("WATCH", "key")
+ c.Do("MULTI")
+ c.Do("PING")
+ c.Close()
+
+ want = []string{"WATCH", "MULTI", "PING", "DISCARD"}
+ if !reflect.DeepEqual(d.commands, want) {
+ t.Errorf("got commands %v, want %v", d.commands, want)
+ }
+ d.commands = nil
+
+ c = p.Get()
+ c.Do("WATCH", "key")
+ c.Do("MULTI")
+ c.Do("DISCARD")
+ c.Do("PING")
+ c.Close()
+
+ want = []string{"WATCH", "MULTI", "DISCARD", "PING"}
+ if !reflect.DeepEqual(d.commands, want) {
+ t.Errorf("got commands %v, want %v", d.commands, want)
+ }
+ d.commands = nil
+
+ c = p.Get()
+ c.Do("WATCH", "key")
+ c.Do("MULTI")
+ c.Do("EXEC")
+ c.Do("PING")
+ c.Close()
+
+ want = []string{"WATCH", "MULTI", "EXEC", "PING"}
+ if !reflect.DeepEqual(d.commands, want) {
+ t.Errorf("got commands %v, want %v", d.commands, want)
+ }
+ d.commands = nil
+}
+
+func startGoroutines(p *redis.Pool, cmd string, args ...interface{}) chan error {
+ errs := make(chan error, 10)
+ for i := 0; i < cap(errs); i++ {
+ go func() {
+ c := p.Get()
+ _, err := c.Do(cmd, args...)
+ errs <- err
+ c.Close()
+ }()
+ }
+
+ // Wait for goroutines to block.
+ time.Sleep(time.Second / 4)
+
+ return errs
+}
+
+func TestWaitPool(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 1,
+ MaxActive: 1,
+ Dial: d.dial,
+ Wait: true,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ errs := startGoroutines(p, "PING")
+ d.check("before close", p, 1, 1)
+ c.Close()
+ timeout := time.After(2 * time.Second)
+ for i := 0; i < cap(errs); i++ {
+ select {
+ case err := <-errs:
+ if err != nil {
+ t.Fatal(err)
+ }
+ case <-timeout:
+ t.Fatalf("timeout waiting for blocked goroutine %d", i)
+ }
+ }
+ d.check("done", p, 1, 1)
+}
+
+func TestWaitPoolClose(t *testing.T) {
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 1,
+ MaxActive: 1,
+ Dial: d.dial,
+ Wait: true,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ if _, err := c.Do("PING"); err != nil {
+ t.Fatal(err)
+ }
+ errs := startGoroutines(p, "PING")
+ d.check("before close", p, 1, 1)
+ p.Close()
+ timeout := time.After(2 * time.Second)
+ for i := 0; i < cap(errs); i++ {
+ select {
+ case err := <-errs:
+ switch err {
+ case nil:
+ t.Fatal("blocked goroutine did not get error")
+ case redis.ErrPoolExhausted:
+ t.Fatal("blocked goroutine got pool exhausted error")
+ }
+ case <-timeout:
+ t.Fatal("timeout waiting for blocked goroutine")
+ }
+ }
+ c.Close()
+ d.check("done", p, 1, 0)
+}
+
+func TestWaitPoolCommandError(t *testing.T) {
+ testErr := errors.New("test")
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 1,
+ MaxActive: 1,
+ Dial: d.dial,
+ Wait: true,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ errs := startGoroutines(p, "ERR", testErr)
+ d.check("before close", p, 1, 1)
+ c.Close()
+ timeout := time.After(2 * time.Second)
+ for i := 0; i < cap(errs); i++ {
+ select {
+ case err := <-errs:
+ if err != nil {
+ t.Fatal(err)
+ }
+ case <-timeout:
+ t.Fatalf("timeout waiting for blocked goroutine %d", i)
+ }
+ }
+ d.check("done", p, cap(errs), 0)
+}
+
+func TestWaitPoolDialError(t *testing.T) {
+ testErr := errors.New("test")
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: 1,
+ MaxActive: 1,
+ Dial: d.dial,
+ Wait: true,
+ }
+ defer p.Close()
+
+ c := p.Get()
+ errs := startGoroutines(p, "ERR", testErr)
+ d.check("before close", p, 1, 1)
+
+ d.dialErr = errors.New("dial")
+ c.Close()
+
+ nilCount := 0
+ errCount := 0
+ timeout := time.After(2 * time.Second)
+ for i := 0; i < cap(errs); i++ {
+ select {
+ case err := <-errs:
+ switch err {
+ case nil:
+ nilCount++
+ case d.dialErr:
+ errCount++
+ default:
+ t.Fatalf("expected dial error or nil, got %v", err)
+ }
+ case <-timeout:
+ t.Fatalf("timeout waiting for blocked goroutine %d", i)
+ }
+ }
+ if nilCount != 1 {
+ t.Errorf("expected one nil error, got %d", nilCount)
+ }
+ if errCount != cap(errs)-1 {
+ t.Errorf("expected %d dial erors, got %d", cap(errs)-1, errCount)
+ }
+ d.check("done", p, cap(errs), 0)
+}
+
+// Borrowing requires us to iterate over the idle connections, unlock the pool,
+// and perform a blocking operation to check the connection still works. If
+// TestOnBorrow fails, we must reacquire the lock and continue iteration. This
+// test ensures that iteration will work correctly if multiple threads are
+// iterating simultaneously.
+func TestLocking_TestOnBorrowFails_PoolDoesntCrash(t *testing.T) {
+ const count = 100
+
+ // First we'll Create a pool where the pilfering of idle connections fails.
+ d := poolDialer{t: t}
+ p := &redis.Pool{
+ MaxIdle: count,
+ MaxActive: count,
+ Dial: d.dial,
+ TestOnBorrow: func(c redis.Conn, t time.Time) error {
+ return errors.New("No way back into the real world.")
+ },
+ }
+ defer p.Close()
+
+ // Fill the pool with idle connections.
+ conns := make([]redis.Conn, count)
+ for i := range conns {
+ conns[i] = p.Get()
+ }
+ for i := range conns {
+ conns[i].Close()
+ }
+
+ // Spawn a bunch of goroutines to thrash the pool.
+ var wg sync.WaitGroup
+ wg.Add(count)
+ for i := 0; i < count; i++ {
+ go func() {
+ c := p.Get()
+ if c.Err() != nil {
+ t.Errorf("pool get failed: %v", c.Err())
+ }
+ c.Close()
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ if d.dialed != count*2 {
+ t.Errorf("Expected %d dials, got %d", count*2, d.dialed)
+ }
+}
+
+func BenchmarkPoolGet(b *testing.B) {
+ b.StopTimer()
+ p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
+ c := p.Get()
+ if err := c.Err(); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ defer p.Close()
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ c = p.Get()
+ c.Close()
+ }
+}
+
+func BenchmarkPoolGetErr(b *testing.B) {
+ b.StopTimer()
+ p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
+ c := p.Get()
+ if err := c.Err(); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ defer p.Close()
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ c = p.Get()
+ if err := c.Err(); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ }
+}
+
+func BenchmarkPoolGetPing(b *testing.B) {
+ b.StopTimer()
+ p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
+ c := p.Get()
+ if err := c.Err(); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ defer p.Close()
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ c = p.Get()
+ if _, err := c.Do("PING"); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ }
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/pubsub_test.go b/vendor/github.com/garyburd/redigo/redis/pubsub_test.go
new file mode 100644
index 000000000..b95513155
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/pubsub_test.go
@@ -0,0 +1,148 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis_test
+
+import (
+ "fmt"
+ "reflect"
+ "sync"
+ "testing"
+
+ "github.com/garyburd/redigo/redis"
+)
+
+func publish(channel, value interface{}) {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+ c.Do("PUBLISH", channel, value)
+}
+
+// Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine.
+func ExamplePubSubConn() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+ var wg sync.WaitGroup
+ wg.Add(2)
+
+ psc := redis.PubSubConn{Conn: c}
+
+ // This goroutine receives and prints pushed notifications from the server.
+ // The goroutine exits when the connection is unsubscribed from all
+ // channels or there is an error.
+ go func() {
+ defer wg.Done()
+ for {
+ switch n := psc.Receive().(type) {
+ case redis.Message:
+ fmt.Printf("Message: %s %s\n", n.Channel, n.Data)
+ case redis.PMessage:
+ fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data)
+ case redis.Subscription:
+ fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count)
+ if n.Count == 0 {
+ return
+ }
+ case error:
+ fmt.Printf("error: %v\n", n)
+ return
+ }
+ }
+ }()
+
+ // This goroutine manages subscriptions for the connection.
+ go func() {
+ defer wg.Done()
+
+ psc.Subscribe("example")
+ psc.PSubscribe("p*")
+
+ // The following function calls publish a message using another
+ // connection to the Redis server.
+ publish("example", "hello")
+ publish("example", "world")
+ publish("pexample", "foo")
+ publish("pexample", "bar")
+
+ // Unsubscribe from all connections. This will cause the receiving
+ // goroutine to exit.
+ psc.Unsubscribe()
+ psc.PUnsubscribe()
+ }()
+
+ wg.Wait()
+
+ // Output:
+ // Subscription: subscribe example 1
+ // Subscription: psubscribe p* 2
+ // Message: example hello
+ // Message: example world
+ // PMessage: p* pexample foo
+ // PMessage: p* pexample bar
+ // Subscription: unsubscribe example 1
+ // Subscription: punsubscribe p* 0
+}
+
+func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) {
+ actual := c.Receive()
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("%s = %v, want %v", message, actual, expected)
+ }
+}
+
+func TestPushed(t *testing.T) {
+ pc, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer pc.Close()
+
+ sc, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer sc.Close()
+
+ c := redis.PubSubConn{Conn: sc}
+
+ c.Subscribe("c1")
+ expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1})
+ c.Subscribe("c2")
+ expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2})
+ c.PSubscribe("p1")
+ expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3})
+ c.PSubscribe("p2")
+ expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4})
+ c.PUnsubscribe()
+ expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3})
+ expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2})
+
+ pc.Do("PUBLISH", "c1", "hello")
+ expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")})
+
+ c.Ping("hello")
+ expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"})
+
+ c.Conn.Send("PING")
+ c.Conn.Flush()
+ expectPushed(t, c, `Send("PING")`, redis.Pong{})
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/reply_test.go b/vendor/github.com/garyburd/redigo/redis/reply_test.go
new file mode 100644
index 000000000..2c774866d
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/reply_test.go
@@ -0,0 +1,179 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis_test
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/garyburd/redigo/redis"
+)
+
+type valueError struct {
+ v interface{}
+ err error
+}
+
+func ve(v interface{}, err error) valueError {
+ return valueError{v, err}
+}
+
+var replyTests = []struct {
+ name interface{}
+ actual valueError
+ expected valueError
+}{
+ {
+ "ints([v1, v2])",
+ ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)),
+ ve([]int{4, 5}, nil),
+ },
+ {
+ "ints(nil)",
+ ve(redis.Ints(nil, nil)),
+ ve([]int(nil), redis.ErrNil),
+ },
+ {
+ "strings([v1, v2])",
+ ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
+ ve([]string{"v1", "v2"}, nil),
+ },
+ {
+ "strings(nil)",
+ ve(redis.Strings(nil, nil)),
+ ve([]string(nil), redis.ErrNil),
+ },
+ {
+ "byteslices([v1, v2])",
+ ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
+ ve([][]byte{[]byte("v1"), []byte("v2")}, nil),
+ },
+ {
+ "byteslices(nil)",
+ ve(redis.ByteSlices(nil, nil)),
+ ve([][]byte(nil), redis.ErrNil),
+ },
+ {
+ "values([v1, v2])",
+ ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
+ ve([]interface{}{[]byte("v1"), []byte("v2")}, nil),
+ },
+ {
+ "values(nil)",
+ ve(redis.Values(nil, nil)),
+ ve([]interface{}(nil), redis.ErrNil),
+ },
+ {
+ "float64(1.0)",
+ ve(redis.Float64([]byte("1.0"), nil)),
+ ve(float64(1.0), nil),
+ },
+ {
+ "float64(nil)",
+ ve(redis.Float64(nil, nil)),
+ ve(float64(0.0), redis.ErrNil),
+ },
+ {
+ "uint64(1)",
+ ve(redis.Uint64(int64(1), nil)),
+ ve(uint64(1), nil),
+ },
+ {
+ "uint64(-1)",
+ ve(redis.Uint64(int64(-1), nil)),
+ ve(uint64(0), redis.ErrNegativeInt),
+ },
+}
+
+func TestReply(t *testing.T) {
+ for _, rt := range replyTests {
+ if rt.actual.err != rt.expected.err {
+ t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err)
+ continue
+ }
+ if !reflect.DeepEqual(rt.actual.v, rt.expected.v) {
+ t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v)
+ }
+ }
+}
+
+// dial wraps DialDefaultServer() with a more suitable function name for examples.
+func dial() (redis.Conn, error) {
+ return redis.DialDefaultServer()
+}
+
+func ExampleBool() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+
+ c.Do("SET", "foo", 1)
+ exists, _ := redis.Bool(c.Do("EXISTS", "foo"))
+ fmt.Printf("%#v\n", exists)
+ // Output:
+ // true
+}
+
+func ExampleInt() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+
+ c.Do("SET", "k1", 1)
+ n, _ := redis.Int(c.Do("GET", "k1"))
+ fmt.Printf("%#v\n", n)
+ n, _ = redis.Int(c.Do("INCR", "k1"))
+ fmt.Printf("%#v\n", n)
+ // Output:
+ // 1
+ // 2
+}
+
+func ExampleInts() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+
+ c.Do("SADD", "set_with_integers", 4, 5, 6)
+ ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers"))
+ fmt.Printf("%#v\n", ints)
+ // Output:
+ // []int{4, 5, 6}
+}
+
+func ExampleString() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+
+ c.Do("SET", "hello", "world")
+ s, err := redis.String(c.Do("GET", "hello"))
+ fmt.Printf("%#v\n", s)
+ // Output:
+ // "world"
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/scan_test.go b/vendor/github.com/garyburd/redigo/redis/scan_test.go
new file mode 100644
index 000000000..d364dff42
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/scan_test.go
@@ -0,0 +1,440 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis_test
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "testing"
+
+ "github.com/garyburd/redigo/redis"
+)
+
+var scanConversionTests = []struct {
+ src interface{}
+ dest interface{}
+}{
+ {[]byte("-inf"), math.Inf(-1)},
+ {[]byte("+inf"), math.Inf(1)},
+ {[]byte("0"), float64(0)},
+ {[]byte("3.14159"), float64(3.14159)},
+ {[]byte("3.14"), float32(3.14)},
+ {[]byte("-100"), int(-100)},
+ {[]byte("101"), int(101)},
+ {int64(102), int(102)},
+ {[]byte("103"), uint(103)},
+ {int64(104), uint(104)},
+ {[]byte("105"), int8(105)},
+ {int64(106), int8(106)},
+ {[]byte("107"), uint8(107)},
+ {int64(108), uint8(108)},
+ {[]byte("0"), false},
+ {int64(0), false},
+ {[]byte("f"), false},
+ {[]byte("1"), true},
+ {int64(1), true},
+ {[]byte("t"), true},
+ {"hello", "hello"},
+ {[]byte("hello"), "hello"},
+ {[]byte("world"), []byte("world")},
+ {[]interface{}{[]byte("foo")}, []interface{}{[]byte("foo")}},
+ {[]interface{}{[]byte("foo")}, []string{"foo"}},
+ {[]interface{}{[]byte("hello"), []byte("world")}, []string{"hello", "world"}},
+ {[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}},
+ {[]interface{}{[]byte("1")}, []int{1}},
+ {[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}},
+ {[]interface{}{[]byte("1"), []byte("2")}, []float64{1, 2}},
+ {[]interface{}{[]byte("1")}, []byte{1}},
+ {[]interface{}{[]byte("1")}, []bool{true}},
+}
+
+func TestScanConversion(t *testing.T) {
+ for _, tt := range scanConversionTests {
+ values := []interface{}{tt.src}
+ dest := reflect.New(reflect.TypeOf(tt.dest))
+ values, err := redis.Scan(values, dest.Interface())
+ if err != nil {
+ t.Errorf("Scan(%v) returned error %v", tt, err)
+ continue
+ }
+ if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) {
+ t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest)
+ }
+ }
+}
+
+var scanConversionErrorTests = []struct {
+ src interface{}
+ dest interface{}
+}{
+ {[]byte("1234"), byte(0)},
+ {int64(1234), byte(0)},
+ {[]byte("-1"), byte(0)},
+ {int64(-1), byte(0)},
+ {[]byte("junk"), false},
+ {redis.Error("blah"), false},
+}
+
+func TestScanConversionError(t *testing.T) {
+ for _, tt := range scanConversionErrorTests {
+ values := []interface{}{tt.src}
+ dest := reflect.New(reflect.TypeOf(tt.dest))
+ values, err := redis.Scan(values, dest.Interface())
+ if err == nil {
+ t.Errorf("Scan(%v) did not return error", tt)
+ }
+ }
+}
+
+func ExampleScan() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+
+ c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
+ c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
+ c.Send("HMSET", "album:3", "title", "Beat")
+ c.Send("LPUSH", "albums", "1")
+ c.Send("LPUSH", "albums", "2")
+ c.Send("LPUSH", "albums", "3")
+ values, err := redis.Values(c.Do("SORT", "albums",
+ "BY", "album:*->rating",
+ "GET", "album:*->title",
+ "GET", "album:*->rating"))
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ for len(values) > 0 {
+ var title string
+ rating := -1 // initialize to illegal value to detect nil.
+ values, err = redis.Scan(values, &title, &rating)
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ if rating == -1 {
+ fmt.Println(title, "not-rated")
+ } else {
+ fmt.Println(title, rating)
+ }
+ }
+ // Output:
+ // Beat not-rated
+ // Earthbound 1
+ // Red 5
+}
+
+type s0 struct {
+ X int
+ Y int `redis:"y"`
+ Bt bool
+}
+
+type s1 struct {
+ X int `redis:"-"`
+ I int `redis:"i"`
+ U uint `redis:"u"`
+ S string `redis:"s"`
+ P []byte `redis:"p"`
+ B bool `redis:"b"`
+ Bt bool
+ Bf bool
+ s0
+}
+
+var scanStructTests = []struct {
+ title string
+ reply []string
+ value interface{}
+}{
+ {"basic",
+ []string{"i", "-1234", "u", "5678", "s", "hello", "p", "world", "b", "t", "Bt", "1", "Bf", "0", "X", "123", "y", "456"},
+ &s1{I: -1234, U: 5678, S: "hello", P: []byte("world"), B: true, Bt: true, Bf: false, s0: s0{X: 123, Y: 456}},
+ },
+}
+
+func TestScanStruct(t *testing.T) {
+ for _, tt := range scanStructTests {
+
+ var reply []interface{}
+ for _, v := range tt.reply {
+ reply = append(reply, []byte(v))
+ }
+
+ value := reflect.New(reflect.ValueOf(tt.value).Type().Elem())
+
+ if err := redis.ScanStruct(reply, value.Interface()); err != nil {
+ t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err)
+ }
+
+ if !reflect.DeepEqual(value.Interface(), tt.value) {
+ t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value)
+ }
+ }
+}
+
+func TestBadScanStructArgs(t *testing.T) {
+ x := []interface{}{"A", "b"}
+ test := func(v interface{}) {
+ if err := redis.ScanStruct(x, v); err == nil {
+ t.Errorf("Expect error for ScanStruct(%T, %T)", x, v)
+ }
+ }
+
+ test(nil)
+
+ var v0 *struct{}
+ test(v0)
+
+ var v1 int
+ test(&v1)
+
+ x = x[:1]
+ v2 := struct{ A string }{}
+ test(&v2)
+}
+
+var scanSliceTests = []struct {
+ src []interface{}
+ fieldNames []string
+ ok bool
+ dest interface{}
+}{
+ {
+ []interface{}{[]byte("1"), nil, []byte("-1")},
+ nil,
+ true,
+ []int{1, 0, -1},
+ },
+ {
+ []interface{}{[]byte("1"), nil, []byte("2")},
+ nil,
+ true,
+ []uint{1, 0, 2},
+ },
+ {
+ []interface{}{[]byte("-1")},
+ nil,
+ false,
+ []uint{1},
+ },
+ {
+ []interface{}{[]byte("hello"), nil, []byte("world")},
+ nil,
+ true,
+ [][]byte{[]byte("hello"), nil, []byte("world")},
+ },
+ {
+ []interface{}{[]byte("hello"), nil, []byte("world")},
+ nil,
+ true,
+ []string{"hello", "", "world"},
+ },
+ {
+ []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
+ nil,
+ true,
+ []struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
+ },
+ {
+ []interface{}{[]byte("a1"), []byte("b1")},
+ nil,
+ false,
+ []struct{ A, B, C string }{{"a1", "b1", ""}},
+ },
+ {
+ []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
+ nil,
+ true,
+ []*struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
+ },
+ {
+ []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
+ []string{"A", "B"},
+ true,
+ []struct{ A, C, B string }{{"a1", "", "b1"}, {"a2", "", "b2"}},
+ },
+ {
+ []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
+ nil,
+ false,
+ []struct{}{},
+ },
+}
+
+func TestScanSlice(t *testing.T) {
+ for _, tt := range scanSliceTests {
+
+ typ := reflect.ValueOf(tt.dest).Type()
+ dest := reflect.New(typ)
+
+ err := redis.ScanSlice(tt.src, dest.Interface(), tt.fieldNames...)
+ if tt.ok != (err == nil) {
+ t.Errorf("ScanSlice(%v, []%s, %v) returned error %v", tt.src, typ, tt.fieldNames, err)
+ continue
+ }
+ if tt.ok && !reflect.DeepEqual(dest.Elem().Interface(), tt.dest) {
+ t.Errorf("ScanSlice(src, []%s) returned %#v, want %#v", typ, dest.Elem().Interface(), tt.dest)
+ }
+ }
+}
+
+func ExampleScanSlice() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+
+ c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
+ c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
+ c.Send("HMSET", "album:3", "title", "Beat", "rating", 4)
+ c.Send("LPUSH", "albums", "1")
+ c.Send("LPUSH", "albums", "2")
+ c.Send("LPUSH", "albums", "3")
+ values, err := redis.Values(c.Do("SORT", "albums",
+ "BY", "album:*->rating",
+ "GET", "album:*->title",
+ "GET", "album:*->rating"))
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ var albums []struct {
+ Title string
+ Rating int
+ }
+ if err := redis.ScanSlice(values, &albums); err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Printf("%v\n", albums)
+ // Output:
+ // [{Earthbound 1} {Beat 4} {Red 5}]
+}
+
+var argsTests = []struct {
+ title string
+ actual redis.Args
+ expected redis.Args
+}{
+ {"struct ptr",
+ redis.Args{}.AddFlat(&struct {
+ I int `redis:"i"`
+ U uint `redis:"u"`
+ S string `redis:"s"`
+ P []byte `redis:"p"`
+ M map[string]string `redis:"m"`
+ Bt bool
+ Bf bool
+ }{
+ -1234, 5678, "hello", []byte("world"), map[string]string{"hello": "world"}, true, false,
+ }),
+ redis.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "m", map[string]string{"hello": "world"}, "Bt", true, "Bf", false},
+ },
+ {"struct",
+ redis.Args{}.AddFlat(struct{ I int }{123}),
+ redis.Args{"I", 123},
+ },
+ {"slice",
+ redis.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2),
+ redis.Args{1, "a", "b", "c", 2},
+ },
+ {"struct omitempty",
+ redis.Args{}.AddFlat(&struct {
+ I int `redis:"i,omitempty"`
+ U uint `redis:"u,omitempty"`
+ S string `redis:"s,omitempty"`
+ P []byte `redis:"p,omitempty"`
+ M map[string]string `redis:"m,omitempty"`
+ Bt bool `redis:"Bt,omitempty"`
+ Bf bool `redis:"Bf,omitempty"`
+ }{
+ 0, 0, "", []byte{}, map[string]string{}, true, false,
+ }),
+ redis.Args{"Bt", true},
+ },
+}
+
+func TestArgs(t *testing.T) {
+ for _, tt := range argsTests {
+ if !reflect.DeepEqual(tt.actual, tt.expected) {
+ t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected)
+ }
+ }
+}
+
+func ExampleArgs() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+
+ var p1, p2 struct {
+ Title string `redis:"title"`
+ Author string `redis:"author"`
+ Body string `redis:"body"`
+ }
+
+ p1.Title = "Example"
+ p1.Author = "Gary"
+ p1.Body = "Hello"
+
+ if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ m := map[string]string{
+ "title": "Example2",
+ "author": "Steve",
+ "body": "Map",
+ }
+
+ if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ for _, id := range []string{"id1", "id2"} {
+
+ v, err := redis.Values(c.Do("HGETALL", id))
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ if err := redis.ScanStruct(v, &p2); err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ fmt.Printf("%+v\n", p2)
+ }
+
+ // Output:
+ // {Title:Example Author:Gary Body:Hello}
+ // {Title:Example2 Author:Steve Body:Map}
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/script_test.go b/vendor/github.com/garyburd/redigo/redis/script_test.go
new file mode 100644
index 000000000..af282415c
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/script_test.go
@@ -0,0 +1,100 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis_test
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/garyburd/redigo/redis"
+)
+
+var (
+ // These variables are declared at package level to remove distracting
+ // details from the examples.
+ c redis.Conn
+ reply interface{}
+ err error
+)
+
+func ExampleScript() {
+ // Initialize a package-level variable with a script.
+ var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`)
+
+ // In a function, use the script Do method to evaluate the script. The Do
+ // method optimistically uses the EVALSHA command. If the script is not
+ // loaded, then the Do method falls back to the EVAL command.
+ reply, err = getScript.Do(c, "foo")
+}
+
+func TestScript(t *testing.T) {
+ c, err := redis.DialDefaultServer()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+
+ // To test fall back in Do, we make script unique by adding comment with current time.
+ script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano())
+ s := redis.NewScript(2, script)
+ reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")}
+
+ v, err := s.Do(c, "key1", "key2", "arg1", "arg2")
+ if err != nil {
+ t.Errorf("s.Do(c, ...) returned %v", err)
+ }
+
+ if !reflect.DeepEqual(v, reply) {
+ t.Errorf("s.Do(c, ..); = %v, want %v", v, reply)
+ }
+
+ err = s.Load(c)
+ if err != nil {
+ t.Errorf("s.Load(c) returned %v", err)
+ }
+
+ err = s.SendHash(c, "key1", "key2", "arg1", "arg2")
+ if err != nil {
+ t.Errorf("s.SendHash(c, ...) returned %v", err)
+ }
+
+ err = c.Flush()
+ if err != nil {
+ t.Errorf("c.Flush() returned %v", err)
+ }
+
+ v, err = c.Receive()
+ if !reflect.DeepEqual(v, reply) {
+ t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply)
+ }
+
+ err = s.Send(c, "key1", "key2", "arg1", "arg2")
+ if err != nil {
+ t.Errorf("s.Send(c, ...) returned %v", err)
+ }
+
+ err = c.Flush()
+ if err != nil {
+ t.Errorf("c.Flush() returned %v", err)
+ }
+
+ v, err = c.Receive()
+ if !reflect.DeepEqual(v, reply) {
+ t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply)
+ }
+
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/test_test.go b/vendor/github.com/garyburd/redigo/redis/test_test.go
new file mode 100644
index 000000000..7240fa1f3
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/test_test.go
@@ -0,0 +1,177 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis
+
+import (
+ "bufio"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+)
+
+func SetNowFunc(f func() time.Time) {
+ nowFunc = f
+}
+
+var (
+ ErrNegativeInt = errNegativeInt
+
+ serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary")
+ serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers")
+ serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`")
+ serverLog = ioutil.Discard
+
+ defaultServerMu sync.Mutex
+ defaultServer *Server
+ defaultServerErr error
+)
+
+type Server struct {
+ name string
+ cmd *exec.Cmd
+ done chan struct{}
+}
+
+func NewServer(name string, args ...string) (*Server, error) {
+ s := &Server{
+ name: name,
+ cmd: exec.Command(*serverPath, args...),
+ done: make(chan struct{}),
+ }
+
+ r, err := s.cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+
+ err = s.cmd.Start()
+ if err != nil {
+ return nil, err
+ }
+
+ ready := make(chan error, 1)
+ go s.watch(r, ready)
+
+ select {
+ case err = <-ready:
+ case <-time.After(time.Second * 10):
+ err = errors.New("timeout waiting for server to start")
+ }
+
+ if err != nil {
+ s.Stop()
+ return nil, err
+ }
+
+ return s, nil
+}
+
+func (s *Server) watch(r io.Reader, ready chan error) {
+ fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name)
+ var listening bool
+ var text string
+ scn := bufio.NewScanner(r)
+ for scn.Scan() {
+ text = scn.Text()
+ fmt.Fprintf(serverLog, "%s\n", text)
+ if !listening {
+ if strings.Contains(text, "The server is now ready to accept connections on port") {
+ listening = true
+ ready <- nil
+ }
+ }
+ }
+ if !listening {
+ ready <- fmt.Errorf("server exited: %s", text)
+ }
+ s.cmd.Wait()
+ fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name)
+ close(s.done)
+}
+
+func (s *Server) Stop() {
+ s.cmd.Process.Signal(os.Interrupt)
+ <-s.done
+}
+
+// stopDefaultServer stops the server created by DialDefaultServer.
+func stopDefaultServer() {
+ defaultServerMu.Lock()
+ defer defaultServerMu.Unlock()
+ if defaultServer != nil {
+ defaultServer.Stop()
+ defaultServer = nil
+ }
+}
+
+// startDefaultServer starts the default server if not already running.
+func startDefaultServer() error {
+ defaultServerMu.Lock()
+ defer defaultServerMu.Unlock()
+ if defaultServer != nil || defaultServerErr != nil {
+ return defaultServerErr
+ }
+ defaultServer, defaultServerErr = NewServer(
+ "default",
+ "--port", strconv.Itoa(*serverBasePort),
+ "--save", "",
+ "--appendonly", "no")
+ return defaultServerErr
+}
+
+// DialDefaultServer starts the test server if not already started and dials a
+// connection to the server.
+func DialDefaultServer() (Conn, error) {
+ if err := startDefaultServer(); err != nil {
+ return nil, err
+ }
+ c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second))
+ if err != nil {
+ return nil, err
+ }
+ c.Do("FLUSHDB")
+ return c, nil
+}
+
+func TestMain(m *testing.M) {
+ os.Exit(func() int {
+ flag.Parse()
+
+ var f *os.File
+ if *serverLogName != "" {
+ var err error
+ f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err)
+ return 1
+ }
+ defer f.Close()
+ serverLog = f
+ }
+
+ defer stopDefaultServer()
+
+ return m.Run()
+ }())
+}
diff --git a/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go b/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go
new file mode 100644
index 000000000..1d86ee6ce
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redis/zpop_example_test.go
@@ -0,0 +1,113 @@
+// Copyright 2013 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redis_test
+
+import (
+ "fmt"
+ "github.com/garyburd/redigo/redis"
+)
+
+// zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands.
+func zpop(c redis.Conn, key string) (result string, err error) {
+
+ defer func() {
+ // Return connection to normal state on error.
+ if err != nil {
+ c.Do("DISCARD")
+ }
+ }()
+
+ // Loop until transaction is successful.
+ for {
+ if _, err := c.Do("WATCH", key); err != nil {
+ return "", err
+ }
+
+ members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0))
+ if err != nil {
+ return "", err
+ }
+ if len(members) != 1 {
+ return "", redis.ErrNil
+ }
+
+ c.Send("MULTI")
+ c.Send("ZREM", key, members[0])
+ queued, err := c.Do("EXEC")
+ if err != nil {
+ return "", err
+ }
+
+ if queued != nil {
+ result = members[0]
+ break
+ }
+ }
+
+ return result, nil
+}
+
+// zpopScript pops a value from a ZSET.
+var zpopScript = redis.NewScript(1, `
+ local r = redis.call('ZRANGE', KEYS[1], 0, 0)
+ if r ~= nil then
+ r = r[1]
+ redis.call('ZREM', KEYS[1], r)
+ end
+ return r
+`)
+
+// This example implements ZPOP as described at
+// http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting.
+func Example_zpop() {
+ c, err := dial()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ defer c.Close()
+
+ // Add test data using a pipeline.
+
+ for i, member := range []string{"red", "blue", "green"} {
+ c.Send("ZADD", "zset", i, member)
+ }
+ if _, err := c.Do(""); err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ // Pop using WATCH/MULTI/EXEC
+
+ v, err := zpop(c, "zset")
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(v)
+
+ // Pop using a script.
+
+ v, err = redis.String(zpopScript.Do(c, "zset"))
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(v)
+
+ // Output:
+ // red
+ // blue
+}
diff --git a/vendor/github.com/garyburd/redigo/redisx/connmux.go b/vendor/github.com/garyburd/redigo/redisx/connmux.go
new file mode 100644
index 000000000..af2cced3f
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redisx/connmux.go
@@ -0,0 +1,152 @@
+// Copyright 2014 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redisx
+
+import (
+ "errors"
+ "sync"
+
+ "github.com/garyburd/redigo/internal"
+ "github.com/garyburd/redigo/redis"
+)
+
+// ConnMux multiplexes one or more connections to a single underlying
+// connection. The ConnMux connections do not support concurrency, commands
+// that associate server side state with the connection or commands that put
+// the connection in a special mode.
+type ConnMux struct {
+ c redis.Conn
+
+ sendMu sync.Mutex
+ sendID uint
+
+ recvMu sync.Mutex
+ recvID uint
+ recvWait map[uint]chan struct{}
+}
+
+func NewConnMux(c redis.Conn) *ConnMux {
+ return &ConnMux{c: c, recvWait: make(map[uint]chan struct{})}
+}
+
+// Get gets a connection. The application must close the returned connection.
+func (p *ConnMux) Get() redis.Conn {
+ c := &muxConn{p: p}
+ c.ids = c.buf[:0]
+ return c
+}
+
+// Close closes the underlying connection.
+func (p *ConnMux) Close() error {
+ return p.c.Close()
+}
+
+type muxConn struct {
+ p *ConnMux
+ ids []uint
+ buf [8]uint
+}
+
+func (c *muxConn) send(flush bool, cmd string, args ...interface{}) error {
+ if internal.LookupCommandInfo(cmd).Set != 0 {
+ return errors.New("command not supported by mux pool")
+ }
+ p := c.p
+ p.sendMu.Lock()
+ id := p.sendID
+ c.ids = append(c.ids, id)
+ p.sendID++
+ err := p.c.Send(cmd, args...)
+ if flush {
+ err = p.c.Flush()
+ }
+ p.sendMu.Unlock()
+ return err
+}
+
+func (c *muxConn) Send(cmd string, args ...interface{}) error {
+ return c.send(false, cmd, args...)
+}
+
+func (c *muxConn) Flush() error {
+ p := c.p
+ p.sendMu.Lock()
+ err := p.c.Flush()
+ p.sendMu.Unlock()
+ return err
+}
+
+func (c *muxConn) Receive() (interface{}, error) {
+ if len(c.ids) == 0 {
+ return nil, errors.New("mux pool underflow")
+ }
+
+ id := c.ids[0]
+ c.ids = c.ids[1:]
+ if len(c.ids) == 0 {
+ c.ids = c.buf[:0]
+ }
+
+ p := c.p
+ p.recvMu.Lock()
+ if p.recvID != id {
+ ch := make(chan struct{})
+ p.recvWait[id] = ch
+ p.recvMu.Unlock()
+ <-ch
+ p.recvMu.Lock()
+ if p.recvID != id {
+ panic("out of sync")
+ }
+ }
+
+ v, err := p.c.Receive()
+
+ id++
+ p.recvID = id
+ ch, ok := p.recvWait[id]
+ if ok {
+ delete(p.recvWait, id)
+ }
+ p.recvMu.Unlock()
+ if ok {
+ ch <- struct{}{}
+ }
+
+ return v, err
+}
+
+func (c *muxConn) Close() error {
+ var err error
+ if len(c.ids) == 0 {
+ return nil
+ }
+ c.Flush()
+ for _ = range c.ids {
+ _, err = c.Receive()
+ }
+ return err
+}
+
+func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, error) {
+ if err := c.send(true, cmd, args...); err != nil {
+ return nil, err
+ }
+ return c.Receive()
+}
+
+func (c *muxConn) Err() error {
+ return c.p.c.Err()
+}
diff --git a/vendor/github.com/garyburd/redigo/redisx/connmux_test.go b/vendor/github.com/garyburd/redigo/redisx/connmux_test.go
new file mode 100644
index 000000000..9c3c8b162
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redisx/connmux_test.go
@@ -0,0 +1,259 @@
+// Copyright 2014 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package redisx_test
+
+import (
+ "net/textproto"
+ "sync"
+ "testing"
+
+ "github.com/garyburd/redigo/internal/redistest"
+ "github.com/garyburd/redigo/redis"
+ "github.com/garyburd/redigo/redisx"
+)
+
+func TestConnMux(t *testing.T) {
+ c, err := redistest.Dial()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ m := redisx.NewConnMux(c)
+ defer m.Close()
+
+ c1 := m.Get()
+ c2 := m.Get()
+ c1.Send("ECHO", "hello")
+ c2.Send("ECHO", "world")
+ c1.Flush()
+ c2.Flush()
+ s, err := redis.String(c1.Receive())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if s != "hello" {
+ t.Fatalf("echo returned %q, want %q", s, "hello")
+ }
+ s, err = redis.String(c2.Receive())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if s != "world" {
+ t.Fatalf("echo returned %q, want %q", s, "world")
+ }
+ c1.Close()
+ c2.Close()
+}
+
+func TestConnMuxClose(t *testing.T) {
+ c, err := redistest.Dial()
+ if err != nil {
+ t.Fatalf("error connection to database, %v", err)
+ }
+ m := redisx.NewConnMux(c)
+ defer m.Close()
+
+ c1 := m.Get()
+ c2 := m.Get()
+
+ if err := c1.Send("ECHO", "hello"); err != nil {
+ t.Fatal(err)
+ }
+ if err := c1.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := c2.Send("ECHO", "world"); err != nil {
+ t.Fatal(err)
+ }
+ if err := c2.Flush(); err != nil {
+ t.Fatal(err)
+ }
+
+ s, err := redis.String(c2.Receive())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if s != "world" {
+ t.Fatalf("echo returned %q, want %q", s, "world")
+ }
+ c2.Close()
+}
+
+func BenchmarkConn(b *testing.B) {
+ b.StopTimer()
+ c, err := redistest.Dial()
+ if err != nil {
+ b.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ if _, err := c.Do("PING"); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkConnMux(b *testing.B) {
+ b.StopTimer()
+ c, err := redistest.Dial()
+ if err != nil {
+ b.Fatalf("error connection to database, %v", err)
+ }
+ m := redisx.NewConnMux(c)
+ defer m.Close()
+
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ c := m.Get()
+ if _, err := c.Do("PING"); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ }
+}
+
+func BenchmarkPool(b *testing.B) {
+ b.StopTimer()
+
+ p := redis.Pool{Dial: redistest.Dial, MaxIdle: 1}
+ defer p.Close()
+
+ // Fill the pool.
+ c := p.Get()
+ if err := c.Err(); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ c := p.Get()
+ if _, err := c.Do("PING"); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ }
+}
+
+const numConcurrent = 10
+
+func BenchmarkConnMuxConcurrent(b *testing.B) {
+ b.StopTimer()
+ c, err := redistest.Dial()
+ if err != nil {
+ b.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+
+ m := redisx.NewConnMux(c)
+
+ var wg sync.WaitGroup
+ wg.Add(numConcurrent)
+
+ b.StartTimer()
+
+ for i := 0; i < numConcurrent; i++ {
+ go func() {
+ defer wg.Done()
+ for i := 0; i < b.N; i++ {
+ c := m.Get()
+ if _, err := c.Do("PING"); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+func BenchmarkPoolConcurrent(b *testing.B) {
+ b.StopTimer()
+
+ p := redis.Pool{Dial: redistest.Dial, MaxIdle: numConcurrent}
+ defer p.Close()
+
+ // Fill the pool.
+ conns := make([]redis.Conn, numConcurrent)
+ for i := range conns {
+ c := p.Get()
+ if err := c.Err(); err != nil {
+ b.Fatal(err)
+ }
+ conns[i] = c
+ }
+ for _, c := range conns {
+ c.Close()
+ }
+
+ var wg sync.WaitGroup
+ wg.Add(numConcurrent)
+
+ b.StartTimer()
+
+ for i := 0; i < numConcurrent; i++ {
+ go func() {
+ defer wg.Done()
+ for i := 0; i < b.N; i++ {
+ c := p.Get()
+ if _, err := c.Do("PING"); err != nil {
+ b.Fatal(err)
+ }
+ c.Close()
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+func BenchmarkPipelineConcurrency(b *testing.B) {
+ b.StopTimer()
+ c, err := redistest.Dial()
+ if err != nil {
+ b.Fatalf("error connection to database, %v", err)
+ }
+ defer c.Close()
+
+ var wg sync.WaitGroup
+ wg.Add(numConcurrent)
+
+ var pipeline textproto.Pipeline
+
+ b.StartTimer()
+
+ for i := 0; i < numConcurrent; i++ {
+ go func() {
+ defer wg.Done()
+ for i := 0; i < b.N; i++ {
+ id := pipeline.Next()
+ pipeline.StartRequest(id)
+ c.Send("PING")
+ c.Flush()
+ pipeline.EndRequest(id)
+ pipeline.StartResponse(id)
+ _, err := c.Receive()
+ if err != nil {
+ b.Fatal(err)
+ }
+ pipeline.EndResponse(id)
+ }
+ }()
+ }
+ wg.Wait()
+}
diff --git a/vendor/github.com/garyburd/redigo/redisx/doc.go b/vendor/github.com/garyburd/redigo/redisx/doc.go
new file mode 100644
index 000000000..91653dbe2
--- /dev/null
+++ b/vendor/github.com/garyburd/redigo/redisx/doc.go
@@ -0,0 +1,17 @@
+// Copyright 2012 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+// Package redisx contains experimental features for Redigo. Features in this
+// package may be modified or deleted at any time.
+package redisx // import "github.com/garyburd/redigo/redisx"