├── _config.yml ├── .travis.yml ├── vendor └── github.com │ ├── alicebob │ └── miniredis │ │ ├── .travis.yml │ │ ├── Makefile │ │ ├── test.go │ │ ├── LICENSE │ │ ├── example_test.go │ │ ├── cmd_server_test.go │ │ ├── cmd_server.go │ │ ├── keys.go │ │ ├── sorted_set_test.go │ │ ├── sorted_set.go │ │ ├── check.go │ │ ├── cmd_connection_test.go │ │ ├── cmd_connection.go │ │ ├── keys_test.go │ │ ├── cmd_transactions.go │ │ ├── miniredis_test.go │ │ ├── redis.go │ │ ├── README.md │ │ ├── cmd_transactions_test.go │ │ ├── miniredis.go │ │ └── cmd_generic.go │ ├── bsm │ └── redeo │ │ ├── .travis.yml │ │ ├── Makefile │ │ ├── handler.go │ │ ├── client_test.go │ │ ├── redeo.go │ │ ├── _examples │ │ ├── simple.go │ │ └── streaming.go │ │ ├── info │ │ ├── values.go │ │ ├── info_test.go │ │ ├── values_test.go │ │ └── info.go │ │ ├── clients_test.go │ │ ├── config.go │ │ ├── redeo_test.go │ │ ├── clients.go │ │ ├── README.md │ │ ├── info_test.go │ │ ├── client.go │ │ ├── request.go │ │ ├── info.go │ │ ├── request_test.go │ │ ├── server_test.go │ │ ├── server.go │ │ ├── responder_test.go │ │ └── responder.go │ ├── willf │ └── bitset │ │ ├── CHANGELOG │ │ ├── .gitignore │ │ ├── LICENSE.txt │ │ └── README.md │ └── garyburd │ └── redigo │ ├── .travis.yml │ ├── internal │ ├── commandinfo_test.go │ ├── commandinfo.go │ └── redistest │ │ └── testdb.go │ ├── redisx │ ├── doc.go │ ├── connmux.go │ └── connmux_test.go │ ├── redis │ ├── redis.go │ ├── zpop_example_test.go │ ├── script_test.go │ ├── script.go │ ├── log.go │ ├── pubsub.go │ ├── reply_test.go │ ├── pubsub_test.go │ ├── test_test.go │ ├── doc.go │ └── pool.go │ ├── README.markdown │ └── LICENSE ├── glide.yaml ├── .gitignore ├── bitset.go ├── glide.lock ├── redis_bitset_test.go ├── bloom.go ├── README.md ├── redis_bitset.go └── bloom_test.go /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7 5 | - tip 6 | 7 | script: 8 | - go test -cpu=1,2 -v 9 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | install: go get -t 4 | 5 | script: make test 6 | 7 | sudo: false 8 | 9 | go: 10 | - 1.6.3 11 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | script: make 4 | install: 5 | - go get -u -t ./... 6 | go: 7 | - 1.7 8 | - 1.6 9 | - 1.5 10 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/Makefile: -------------------------------------------------------------------------------- 1 | default: vet test 2 | 3 | test: 4 | go test ./... 5 | 6 | vet: 7 | go vet ./... 8 | 9 | bench: 10 | go test ./... -run=NONE --bench=. 11 | 12 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all install test vet 2 | 3 | all: test vet 4 | 5 | install: 6 | go install 7 | 8 | test: 9 | go test 10 | 11 | vet: 12 | go vet 13 | golint . 14 | -------------------------------------------------------------------------------- /vendor/github.com/willf/bitset/CHANGELOG: -------------------------------------------------------------------------------- 1 | 2014-02-14 Version 1.0.0 💗 2 | 3 | - First 'released' version 4 | - includes various fixes by: 5 | - Daniel Lemire 6 | - Todd Vierling 7 | - Zellyn Hunter 8 | - Seyi Ogunyemi 9 | - Simon Menke 10 | - Cenk Altı -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: . 2 | import: 3 | - package: github.com/garyburd/redigo 4 | version: ^1.0.0 5 | subpackages: 6 | - redis 7 | - package: github.com/willf/bitset 8 | version: ^1.0.0 9 | - package: github.com/alicebob/miniredis 10 | - package: github.com/bsm/redeo 11 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | services: 4 | - redis-server 5 | 6 | go: 7 | - 1.4 8 | - 1.5 9 | - 1.6 10 | - tip 11 | 12 | script: 13 | - go get -t -v ./... 14 | - diff -u <(echo -n) <(gofmt -d .) 15 | - go vet $(go list ./... | grep -v /vendor/) 16 | - go test -v -race ./... 17 | -------------------------------------------------------------------------------- /vendor/github.com/willf/bitset/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | .idea 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | *.test 25 | *.prof 26 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/handler.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | // Abstract handler interface 4 | type Handler interface { 5 | ServeClient(out *Responder, req *Request) error 6 | } 7 | 8 | // Abstract handler function, a handler client may return an error, which 9 | // will then be returned to the client. 10 | type HandlerFunc func(out *Responder, req *Request) error 11 | 12 | // ServeClient calls f(out, req). 13 | func (f HandlerFunc) ServeClient(out *Responder, req *Request) error { 14 | return f(out, req) 15 | } 16 | -------------------------------------------------------------------------------- /bitset.go: -------------------------------------------------------------------------------- 1 | package bloom 2 | 3 | import ( 4 | "github.com/willf/bitset" 5 | ) 6 | 7 | type BitSet struct { 8 | bitSet *bitset.BitSet 9 | } 10 | 11 | func NewBitSet(m uint) *BitSet { 12 | return &BitSet{bitSet: bitset.New(m)} 13 | } 14 | 15 | func (b *BitSet) Set(offsets []uint) error { 16 | for _, offset := range offsets { 17 | b.bitSet.Set(offset) 18 | } 19 | return nil 20 | } 21 | 22 | func (b *BitSet) Test(offsets []uint) (bool, error) { 23 | for _, offset := range offsets { 24 | if !b.bitSet.Test(offset) { 25 | return false, nil 26 | } 27 | } 28 | 29 | return true, nil 30 | } 31 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: 2eabd49f2643d72f4dda2026a0ec53b96f7fd8a8308c623336bf196dcf6da137 2 | updated: 2017-01-06T21:56:10.010570945-07:00 3 | imports: 4 | - name: github.com/alicebob/miniredis 5 | version: 10ddf01f45bee3c40d1af5dbaad9aa71e6f20835 6 | - name: github.com/bsm/redeo 7 | version: 1ce09fc76693fb3c1ca9b529c66f38920beb6fb8 8 | subpackages: 9 | - info 10 | - name: github.com/garyburd/redigo 11 | version: 8873b2f1995f59d4bcdd2b0dc9858e2cb9bf0c13 12 | subpackages: 13 | - internal 14 | - redis 15 | - name: github.com/willf/bitset 16 | version: 3582df120789e0e260108c11fd01775b30782fa7 17 | testImports: [] 18 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/client_test.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("Client", func() { 9 | var subject *Client 10 | 11 | BeforeEach(func() { 12 | subject = NewClient(&mockConn{Port: 10001}) 13 | }) 14 | 15 | It("should generate IDs", func() { 16 | a, b := NewClient(&mockConn{}), NewClient(&mockConn{}) 17 | Expect(b.ID() - 1).To(Equal(a.ID())) 18 | }) 19 | 20 | It("should generate info string", func() { 21 | subject.id = 12 22 | Expect(subject.String()).To(Equal(`id=12 addr=1.2.3.4:10001 age=0 idle=0 cmd=`)) 23 | }) 24 | 25 | }) 26 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/redeo.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Protocol errors 8 | var ErrInvalidRequest = errors.New("redeo: invalid request") 9 | 10 | // Client errors can be returned by handlers. 11 | // Unlike other errors, client errors do not disconnect the client 12 | type ClientError string 13 | 14 | // Error returns the error message 15 | func (e ClientError) Error() string { 16 | return "redeo: " + string(e) 17 | } 18 | 19 | // UnknownCommand returns an unknown command 20 | func UnknownCommand(command string) ClientError { 21 | return ClientError("unknown command '" + command + "'") 22 | } 23 | 24 | // WrongNumberOfArgs returns an unknown command 25 | func WrongNumberOfArgs(command string) ClientError { 26 | return ClientError("wrong number of arguments for '" + command + "' command") 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/commandinfo_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import "testing" 4 | 5 | func TestLookupCommandInfo(t *testing.T) { 6 | for _, n := range []string{"watch", "WATCH", "wAtch"} { 7 | if LookupCommandInfo(n) == (CommandInfo{}) { 8 | t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n) 9 | } 10 | } 11 | } 12 | 13 | func benchmarkLookupCommandInfo(b *testing.B, names ...string) { 14 | for i := 0; i < b.N; i++ { 15 | for _, c := range names { 16 | LookupCommandInfo(c) 17 | } 18 | } 19 | } 20 | 21 | func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) { 22 | benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR") 23 | } 24 | 25 | func BenchmarkLookupCommandInfoMixedCase(b *testing.B) { 26 | benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR") 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redisx/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Package redisx contains experimental features for Redigo. Features in this 16 | // package may be modified or deleted at any time. 17 | package redisx // import "github.com/garyburd/redigo/redisx" 18 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/_examples/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/bsm/redeo" 7 | ) 8 | 9 | func main() { 10 | srv := redeo.NewServer(nil) 11 | srv.HandleFunc("ping", func(out *redeo.Responder, _ *redeo.Request) error { 12 | out.WriteInlineString("PONG") 13 | return nil 14 | }) 15 | srv.HandleFunc("info", func(out *redeo.Responder, _ *redeo.Request) error { 16 | out.WriteString(srv.Info().String()) 17 | return nil 18 | }) 19 | srv.HandleFunc("client", func(out *redeo.Responder, req *redeo.Request) error { 20 | if len(req.Args) != 1 { 21 | return req.WrongNumberOfArgs() 22 | } 23 | 24 | switch req.Args[0] { 25 | case "list": 26 | out.WriteString(srv.Info().ClientsString()) 27 | default: 28 | return req.UnknownCommand() 29 | } 30 | return nil 31 | }) 32 | 33 | log.Printf("Listening on tcp://%s", srv.Addr()) 34 | log.Fatal(srv.ListenAndServe()) 35 | } 36 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/test.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "reflect" 7 | "runtime" 8 | "testing" 9 | ) 10 | 11 | // assert fails the test if the condition is false. 12 | func assert(tb testing.TB, condition bool, msg string, v ...interface{}) { 13 | if !condition { 14 | _, file, line, _ := runtime.Caller(1) 15 | fmt.Printf("%s:%d: "+msg+"\n", append([]interface{}{filepath.Base(file), line}, v...)...) 16 | tb.FailNow() 17 | } 18 | } 19 | 20 | // ok fails the test if an err is not nil. 21 | func ok(tb testing.TB, err error) { 22 | if err != nil { 23 | _, file, line, _ := runtime.Caller(1) 24 | fmt.Printf("%s:%d: unexpected error: %s\n", filepath.Base(file), line, err.Error()) 25 | tb.FailNow() 26 | } 27 | } 28 | 29 | // equals fails the test if exp is not equal to act. 30 | func equals(tb testing.TB, exp, act interface{}) { 31 | if !reflect.DeepEqual(exp, act) { 32 | _, file, line, _ := runtime.Caller(1) 33 | fmt.Printf("%s:%d: expected: %#v got: %#v\n", filepath.Base(file), line, exp, act) 34 | tb.FailNow() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/info/values.go: -------------------------------------------------------------------------------- 1 | package info 2 | 3 | import ( 4 | "strconv" 5 | "sync/atomic" 6 | ) 7 | 8 | // A value must be exportable as a string 9 | type Value interface { 10 | String() string 11 | } 12 | 13 | // PlainString is the simplest value type 14 | type PlainString string 15 | 16 | func (v PlainString) String() string { return string(v) } 17 | 18 | // PlainInt converts a static integer into a value 19 | func PlainInt(n int) Value { return PlainString(strconv.Itoa(n)) } 20 | 21 | // Callback function 22 | type Callback func() string 23 | 24 | func (c Callback) String() string { return c() } 25 | 26 | // Counter is a numeric counter value 27 | type Counter struct{ v int64 } 28 | 29 | func NewCounter() *Counter { return &Counter{} } 30 | func (c *Counter) Inc(delta int64) int64 { return atomic.AddInt64(&c.v, delta) } 31 | func (c *Counter) Set(v int64) { atomic.StoreInt64(&c.v, v) } 32 | func (c *Counter) Value() int64 { return atomic.LoadInt64(&c.v) } 33 | func (c *Counter) String() string { return strconv.FormatInt(c.Value(), 10) } 34 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Harmen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/clients_test.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("clients", func() { 9 | var subject *clients 10 | 11 | BeforeEach(func() { 12 | subject = newClientRegistry() 13 | }) 14 | 15 | It("should put clients", func() { 16 | subject.Put(NewClient(&mockConn{})) 17 | Expect(subject.Len()).To(Equal(1)) 18 | Expect(subject.All()).To(HaveLen(1)) 19 | }) 20 | 21 | It("should close clients", func() { 22 | conn := &mockConn{} 23 | client := NewClient(conn) 24 | subject.Put(client) 25 | Expect(subject.m).To(HaveLen(1)) 26 | 27 | err := subject.Close(client.id) 28 | Expect(err).NotTo(HaveOccurred()) 29 | Expect(conn.closed).To(BeTrue()) 30 | Expect(subject.m).To(BeEmpty()) 31 | 32 | err = subject.Close(9999) 33 | Expect(err).NotTo(HaveOccurred()) 34 | }) 35 | 36 | It("should clear clients", func() { 37 | conn := &mockConn{} 38 | subject.Put(NewClient(conn)) 39 | Expect(subject.m).To(HaveLen(1)) 40 | 41 | err := subject.Clear() 42 | Expect(err).NotTo(HaveOccurred()) 43 | Expect(conn.closed).To(BeTrue()) 44 | Expect(subject.m).To(BeEmpty()) 45 | }) 46 | 47 | }) 48 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/config.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import "time" 4 | 5 | // Server configuration 6 | type Config struct { 7 | 8 | // Accept connections on the specified port, default is 0.0.0.0:9736. 9 | // If not specified server will not listen on a TCP socket. 10 | Addr string 11 | 12 | // Specify the path for the unix socket that will be used to listen for 13 | // incoming connections. There is no default, so server will not listen 14 | // on a unix socket when not specified. 15 | Socket string 16 | 17 | // Close the connection after a client is idle for N seconds (0 to disable) 18 | Timeout time.Duration 19 | 20 | // If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence 21 | // of communication. This is useful for two reasons: 22 | // 1) Detect dead peers. 23 | // 2) Take the connection alive from the point of view of network 24 | // equipment in the middle. 25 | // On Linux, the specified value (in seconds) is the period used to send ACKs. 26 | // Note that to close the connection the double of the time is needed. 27 | // On other kernels the period depends on the kernel configuration. 28 | TCPKeepAlive time.Duration 29 | } 30 | 31 | // Default configuration is used when nil is passed to NewServer 32 | var DefaultConfig = &Config{ 33 | Addr: "0.0.0.0:9736", 34 | } 35 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/redeo_test.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net" 7 | "testing" 8 | "time" 9 | 10 | . "github.com/onsi/ginkgo" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | // ------------------------------------------------------------------------ 15 | 16 | func TestSuite(t *testing.T) { 17 | RegisterFailHandler(Fail) 18 | RunSpecs(t, "redeo") 19 | } 20 | 21 | // ------------------------------------------------------------------------ 22 | 23 | type badWriter struct{ bytes.Buffer } 24 | 25 | func (w *badWriter) Write(p []byte) (int, error) { 26 | w.Buffer.Write(p) 27 | return 0, io.EOF 28 | } 29 | 30 | type mockConn struct { 31 | bytes.Buffer 32 | Port int 33 | closed bool 34 | } 35 | 36 | func (m *mockConn) Close() error { m.closed = true; return nil } 37 | func (m *mockConn) LocalAddr() net.Addr { 38 | return &net.TCPAddr{IP: net.IP{127, 0, 0, 1}, Port: 9736, Zone: ""} 39 | } 40 | func (m *mockConn) RemoteAddr() net.Addr { 41 | return &net.TCPAddr{IP: net.IP{1, 2, 3, 4}, Port: m.Port, Zone: ""} 42 | } 43 | func (m *mockConn) SetDeadline(_ time.Time) error { return nil } 44 | func (m *mockConn) SetReadDeadline(_ time.Time) error { return nil } 45 | func (m *mockConn) SetWriteDeadline(_ time.Time) error { return nil } 46 | 47 | var _ net.Conn = &mockConn{} 48 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/example_test.go: -------------------------------------------------------------------------------- 1 | package miniredis_test 2 | 3 | import ( 4 | "github.com/alicebob/miniredis" 5 | "github.com/garyburd/redigo/redis" 6 | ) 7 | 8 | func Example() { 9 | s, err := miniredis.Run() 10 | if err != nil { 11 | panic(err) 12 | } 13 | defer s.Close() 14 | 15 | // Configure you application to connect to redis at s.Addr() 16 | // Any redis client should work, as long as you use redis commands which 17 | // miniredis implements. 18 | c, err := redis.Dial("tcp", s.Addr()) 19 | if err != nil { 20 | panic(err) 21 | } 22 | _, err = c.Do("SET", "foo", "bar") 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | // You can ask miniredis about keys directly, without going over the network. 28 | if got, err := s.Get("foo"); err != nil || got != "bar" { 29 | panic("Didn't get 'bar' back") 30 | } 31 | // Or with a DB id 32 | if _, err := s.DB(42).Get("foo"); err != miniredis.ErrKeyNotFound { 33 | panic("Didn't use a different DB") 34 | } 35 | // Or use a Check* function which Fail()s if the key is not what we expect 36 | // (checks for existence, key type and the value) 37 | // s.CheckGet(t, "foo", "bar") 38 | 39 | // Check if there really was only one connection. 40 | if s.TotalConnectionCount() != 1 { 41 | panic("Too many connections made") 42 | } 43 | 44 | // Output: 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/_examples/streaming.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Streaming example: 4 | 5 | $ redis-cli -p 9736 ping 6 | PONG 7 | 8 | $ redis-cli -p 9736 file 9 | (error) ERR wrong number of arguments for 'file' command 10 | $ redis-cli -p 9736 file bad.txt 11 | (error) ERR no such file or directory 12 | 13 | $ echo -n "it works!" > /tmp/hi.txt 14 | $ redis-cli -p 9736 file hi.txt 15 | "it works!" 16 | 17 | */ 18 | package main 19 | 20 | import ( 21 | "log" 22 | "net/http" 23 | "path" 24 | 25 | "github.com/bsm/redeo" 26 | ) 27 | 28 | var root = http.Dir("/tmp") 29 | 30 | func pingCmd(out *redeo.Responder, _ *redeo.Request) error { 31 | out.WriteInlineString("PONG") 32 | return nil 33 | } 34 | 35 | func fileCmd(out *redeo.Responder, req *redeo.Request) error { 36 | if len(req.Args) != 1 { 37 | return req.WrongNumberOfArgs() 38 | } 39 | 40 | file, err := root.Open(path.Clean(req.Args[0])) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | stat, err := file.Stat() 46 | if err != nil { 47 | return err 48 | } 49 | 50 | out.WriteN(file, stat.Size()) 51 | return nil 52 | } 53 | 54 | func main() { 55 | srv := redeo.NewServer(nil) 56 | 57 | srv.HandleFunc("ping", pingCmd) 58 | srv.HandleFunc("file", fileCmd) 59 | 60 | log.Printf("Listening on tcp://%s", srv.Addr()) 61 | log.Fatal(srv.ListenAndServe()) 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/info/info_test.go: -------------------------------------------------------------------------------- 1 | package info 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Registry", func() { 11 | var subject *Registry 12 | 13 | BeforeEach(func() { 14 | subject = New() 15 | subject.Section("Server").Register("version", PlainString("1.0.1")) 16 | subject.Section("Server").Register("date", PlainString("2014-11-11")) 17 | subject.Section("Clients").Register("count", PlainString("17")) 18 | subject.Section("Clients").Register("total", PlainString("123456")) 19 | subject.Section("Empty") 20 | }) 21 | 22 | It("should generate info strings", func() { 23 | Expect(New().String()).To(Equal("")) 24 | Expect(subject.String()).To(Equal("# Server\nversion:1.0.1\ndate:2014-11-11\n\n# Clients\ncount:17\ntotal:123456\n")) 25 | }) 26 | 27 | It("should clear", func() { 28 | subject.Section("Clients").Clear() 29 | Expect(subject.sections[1].kvs).To(BeEmpty()) 30 | subject.Clear() 31 | Expect(subject.sections).To(BeEmpty()) 32 | }) 33 | 34 | }) 35 | 36 | /************************************************************************* 37 | * GINKGO TEST HOOK 38 | *************************************************************************/ 39 | 40 | func TestSuite(t *testing.T) { 41 | RegisterFailHandler(Fail) 42 | RunSpecs(t, "redeo/info") 43 | } 44 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/info/values_test.go: -------------------------------------------------------------------------------- 1 | package info 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("PlainString", func() { 9 | It("should generate strings", func() { 10 | var v Value = PlainString("x") 11 | Expect(v.String()).To(Equal("x")) 12 | }) 13 | }) 14 | 15 | var _ = Describe("PlainInt", func() { 16 | It("should generate strings", func() { 17 | var v Value = PlainInt(12) 18 | Expect(v.String()).To(Equal("12")) 19 | }) 20 | }) 21 | 22 | var _ = Describe("Callback", func() { 23 | It("should generate strings", func() { 24 | var v Value = Callback(func() string { return "x" }) 25 | Expect(v.String()).To(Equal("x")) 26 | }) 27 | }) 28 | 29 | var _ = Describe("Counter", func() { 30 | var subject *Counter 31 | 32 | BeforeEach(func() { 33 | subject = NewCounter() 34 | }) 35 | 36 | It("should have accessors", func() { 37 | Expect(subject.Inc(3)).To(Equal(int64(3))) 38 | Expect(subject.Inc(24)).To(Equal(int64(27))) 39 | Expect(subject.Value()).To(Equal(int64(27))) 40 | Expect(subject.Inc(-17)).To(Equal(int64(10))) 41 | Expect(subject.Value()).To(Equal(int64(10))) 42 | subject.Set(21) 43 | Expect(subject.Value()).To(Equal(int64(21))) 44 | }) 45 | 46 | It("should generate strings", func() { 47 | var v Value = subject 48 | Expect(v.String()).To(Equal("0")) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/clients.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import "sync" 4 | 5 | type clients struct { 6 | m map[uint64]*Client 7 | l sync.Mutex 8 | } 9 | 10 | func newClientRegistry() *clients { 11 | return &clients{ 12 | m: make(map[uint64]*Client, 10), 13 | } 14 | } 15 | 16 | // Put adds a client connection 17 | func (c *clients) Put(client *Client) { 18 | c.l.Lock() 19 | c.m[client.id] = client 20 | c.l.Unlock() 21 | } 22 | 23 | // Close removes a client connection 24 | func (c *clients) Close(id uint64) error { 25 | c.l.Lock() 26 | client, ok := c.m[id] 27 | delete(c.m, id) 28 | c.l.Unlock() 29 | 30 | if ok { 31 | return client.close() 32 | } 33 | return nil 34 | } 35 | 36 | // Clear closes all client connections 37 | func (c *clients) Clear() (err error) { 38 | c.l.Lock() 39 | defer c.l.Unlock() 40 | 41 | for id, conn := range c.m { 42 | if e := conn.close(); e != nil { 43 | err = e 44 | } 45 | delete(c.m, id) 46 | } 47 | return 48 | } 49 | 50 | // Len returns the length 51 | func (c *clients) Len() int { 52 | c.l.Lock() 53 | n := len(c.m) 54 | c.l.Unlock() 55 | return n 56 | } 57 | 58 | // All creates a snapshot of clients 59 | func (c *clients) All() []*Client { 60 | c.l.Lock() 61 | defer c.l.Unlock() 62 | 63 | slice := make([]*Client, 0, len(c.m)) 64 | for _, c := range c.m { 65 | slice = append(slice, c) 66 | } 67 | return slice 68 | } 69 | -------------------------------------------------------------------------------- /redis_bitset_test.go: -------------------------------------------------------------------------------- 1 | package bloom_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/alicebob/miniredis" 8 | "github.com/bculberson/bloom" 9 | "github.com/garyburd/redigo/redis" 10 | ) 11 | 12 | func TestRedisBitSet_New_Set_Test(t *testing.T) { 13 | s, err := miniredis.Run() 14 | if err != nil { 15 | t.Error("Miniredis could not start") 16 | } 17 | defer s.Close() 18 | 19 | pool := &redis.Pool{ 20 | MaxIdle: 3, 21 | IdleTimeout: 240 * time.Second, 22 | Dial: func() (redis.Conn, error) { return redis.Dial("tcp", s.Addr()) }, 23 | } 24 | conn := pool.Get() 25 | defer conn.Close() 26 | 27 | bitSet := bloom.NewRedisBitSet("test_key", 512, conn) 28 | isSetBefore, err := bitSet.Test([]uint{0}) 29 | if err != nil { 30 | t.Error("Could not test bitset in redis") 31 | } 32 | if isSetBefore { 33 | t.Error("Bit should not be set") 34 | } 35 | err = bitSet.Set([]uint{512}) 36 | if err != nil { 37 | t.Error("Could not set bitset in redis") 38 | } 39 | isSetAfter, err := bitSet.Test([]uint{512}) 40 | if err != nil { 41 | t.Error("Could not test bitset in redis") 42 | } 43 | if !isSetAfter { 44 | t.Error("Bit should be set") 45 | } 46 | err = bitSet.Expire(3600) 47 | if err != nil { 48 | t.Errorf("Error adding expiration to bitset: %v", err) 49 | } 50 | err = bitSet.Delete() 51 | if err != nil { 52 | t.Errorf("Error cleaning up bitset: %v", err) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /bloom.go: -------------------------------------------------------------------------------- 1 | package bloom 2 | 3 | import ( 4 | "hash/fnv" 5 | "math" 6 | ) 7 | 8 | type BitSetProvider interface { 9 | Set([]uint) error 10 | Test([]uint) (bool, error) 11 | } 12 | 13 | type BloomFilter struct { 14 | m uint 15 | k uint 16 | bitSet BitSetProvider 17 | } 18 | 19 | func New(m uint, k uint, bitSet BitSetProvider) *BloomFilter { 20 | return &BloomFilter{m: m, k: k, bitSet: bitSet} 21 | } 22 | 23 | func EstimateParameters(n uint, p float64) (uint, uint) { 24 | m := math.Ceil(float64(n) * math.Log(p) / math.Log(1.0/math.Pow(2.0, math.Ln2))) 25 | k := math.Ln2*m/float64(n) + 0.5 26 | 27 | return uint(m), uint(k) 28 | } 29 | 30 | func (f *BloomFilter) Add(data []byte) error { 31 | locations := f.getLocations(data) 32 | err := f.bitSet.Set(locations) 33 | if err != nil { 34 | return err 35 | } 36 | return nil 37 | } 38 | 39 | func (f *BloomFilter) Exists(data []byte) (bool, error) { 40 | locations := f.getLocations(data) 41 | isSet, err := f.bitSet.Test(locations) 42 | if err != nil { 43 | return false, err 44 | } 45 | if !isSet { 46 | return false, nil 47 | } 48 | 49 | return true, nil 50 | } 51 | 52 | func (f *BloomFilter) getLocations(data []byte) []uint { 53 | locations := make([]uint, f.k) 54 | hasher := fnv.New64() 55 | hasher.Write(data) 56 | a := make([]byte, 1) 57 | for i := uint(0); i < f.k; i++ { 58 | a[0] = byte(i) 59 | hasher.Write(a) 60 | hashValue := hasher.Sum64() 61 | locations[i] = uint(hashValue % uint64(f.m)) 62 | } 63 | return locations 64 | } 65 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/cmd_server_test.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/garyburd/redigo/redis" 7 | ) 8 | 9 | // Test DBSIZE, FLUSHDB, and FLUSHALL. 10 | func TestCmdServer(t *testing.T) { 11 | s, err := Run() 12 | ok(t, err) 13 | defer s.Close() 14 | c, err := redis.Dial("tcp", s.Addr()) 15 | ok(t, err) 16 | 17 | // Set something 18 | { 19 | s.Set("aap", "niet") 20 | s.Set("roos", "vuur") 21 | s.DB(1).Set("noot", "mies") 22 | } 23 | 24 | { 25 | n, err := redis.Int(c.Do("DBSIZE")) 26 | ok(t, err) 27 | equals(t, 2, n) 28 | 29 | b, err := redis.String(c.Do("FLUSHDB")) 30 | ok(t, err) 31 | equals(t, "OK", b) 32 | 33 | n, err = redis.Int(c.Do("DBSIZE")) 34 | ok(t, err) 35 | equals(t, 0, n) 36 | 37 | _, err = c.Do("SELECT", 1) 38 | ok(t, err) 39 | 40 | n, err = redis.Int(c.Do("DBSIZE")) 41 | ok(t, err) 42 | equals(t, 1, n) 43 | 44 | b, err = redis.String(c.Do("FLUSHALL")) 45 | ok(t, err) 46 | equals(t, "OK", b) 47 | 48 | n, err = redis.Int(c.Do("DBSIZE")) 49 | ok(t, err) 50 | equals(t, 0, n) 51 | 52 | _, err = c.Do("SELECT", 4) 53 | ok(t, err) 54 | 55 | n, err = redis.Int(c.Do("DBSIZE")) 56 | ok(t, err) 57 | equals(t, 0, n) 58 | 59 | } 60 | 61 | { 62 | _, err := redis.Int(c.Do("DBSIZE", "FOO")) 63 | assert(t, err != nil, "no DBSIZE error") 64 | 65 | _, err = redis.Int(c.Do("FLUSHDB", "FOO")) 66 | assert(t, err != nil, "no FLUSHDB error") 67 | 68 | _, err = redis.Int(c.Do("FLUSHALL", "FOO")) 69 | assert(t, err != nil, "no FLUSHALL error") 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /vendor/github.com/willf/bitset/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Will Fitzgerald. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/cmd_server.go: -------------------------------------------------------------------------------- 1 | // Commands from http://redis.io/commands#server 2 | 3 | package miniredis 4 | 5 | import ( 6 | "github.com/bsm/redeo" 7 | ) 8 | 9 | func commandsServer(m *Miniredis, srv *redeo.Server) { 10 | srv.HandleFunc("DBSIZE", m.cmdDbsize) 11 | srv.HandleFunc("FLUSHALL", m.cmdFlushall) 12 | srv.HandleFunc("FLUSHDB", m.cmdFlushdb) 13 | } 14 | 15 | // DBSIZE 16 | func (m *Miniredis) cmdDbsize(out *redeo.Responder, r *redeo.Request) error { 17 | if len(r.Args) > 0 { 18 | setDirty(r.Client()) 19 | return r.WrongNumberOfArgs() 20 | } 21 | if !m.handleAuth(r.Client(), out) { 22 | return nil 23 | } 24 | 25 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 26 | db := m.db(ctx.selectedDB) 27 | 28 | out.WriteInt(len(db.keys)) 29 | }) 30 | } 31 | 32 | // FLUSHALL 33 | func (m *Miniredis) cmdFlushall(out *redeo.Responder, r *redeo.Request) error { 34 | if len(r.Args) > 0 { 35 | setDirty(r.Client()) 36 | return r.WrongNumberOfArgs() 37 | } 38 | if !m.handleAuth(r.Client(), out) { 39 | return nil 40 | } 41 | 42 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 43 | m.flushAll() 44 | out.WriteOK() 45 | }) 46 | } 47 | 48 | // FLUSHDB 49 | func (m *Miniredis) cmdFlushdb(out *redeo.Responder, r *redeo.Request) error { 50 | if len(r.Args) > 0 { 51 | setDirty(r.Client()) 52 | return r.WrongNumberOfArgs() 53 | } 54 | if !m.handleAuth(r.Client(), out) { 55 | return nil 56 | } 57 | 58 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 59 | m.db(ctx.selectedDB).flush() 60 | out.WriteOK() 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/keys.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | // Translate the 'KEYS' argument ('foo*', 'f??', &c.) into a regexp. 4 | 5 | import ( 6 | "bytes" 7 | "regexp" 8 | ) 9 | 10 | // patternRE compiles a KEYS argument to a regexp. Returns nil if the given 11 | // pattern will never match anything. 12 | // The general strategy is to all non-meta characters between \Q...\E. 13 | func patternRE(k string) *regexp.Regexp { 14 | re := bytes.Buffer{} 15 | re.WriteString(`^\Q`) 16 | for i := 0; i < len(k); i++ { 17 | p := k[i] 18 | switch p { 19 | case '*': 20 | re.WriteString(`\E.*\Q`) 21 | case '?': 22 | re.WriteString(`\E.\Q`) 23 | case '[': 24 | charClass := bytes.Buffer{} 25 | i++ 26 | for ; i < len(k); i++ { 27 | if k[i] == ']' { 28 | break 29 | } 30 | if k[i] == '\\' { 31 | if i == len(k)-1 { 32 | // Ends with a '\'. U-huh. 33 | return nil 34 | } 35 | charClass.WriteByte(k[i]) 36 | i++ 37 | charClass.WriteByte(k[i]) 38 | continue 39 | } 40 | charClass.WriteByte(k[i]) 41 | } 42 | if charClass.Len() == 0 { 43 | // '[]' is valid in Redis, but matches nothing. 44 | return nil 45 | } 46 | re.WriteString(`\E[`) 47 | re.Write(charClass.Bytes()) 48 | re.WriteString(`]\Q`) 49 | 50 | case '\\': 51 | if i == len(k)-1 { 52 | // Ends with a '\'. U-huh. 53 | return nil 54 | } 55 | // Forget the \, keep the next char. 56 | i++ 57 | re.WriteByte(k[i]) 58 | continue 59 | default: 60 | re.WriteByte(p) 61 | } 62 | } 63 | re.WriteString(`\E$`) 64 | return regexp.MustCompile(re.String()) 65 | } 66 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/commandinfo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package internal // import "github.com/garyburd/redigo/internal" 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | const ( 22 | WatchState = 1 << iota 23 | MultiState 24 | SubscribeState 25 | MonitorState 26 | ) 27 | 28 | type CommandInfo struct { 29 | Set, Clear int 30 | } 31 | 32 | var commandInfos = map[string]CommandInfo{ 33 | "WATCH": {Set: WatchState}, 34 | "UNWATCH": {Clear: WatchState}, 35 | "MULTI": {Set: MultiState}, 36 | "EXEC": {Clear: WatchState | MultiState}, 37 | "DISCARD": {Clear: WatchState | MultiState}, 38 | "PSUBSCRIBE": {Set: SubscribeState}, 39 | "SUBSCRIBE": {Set: SubscribeState}, 40 | "MONITOR": {Set: MonitorState}, 41 | } 42 | 43 | func init() { 44 | for n, ci := range commandInfos { 45 | commandInfos[strings.ToLower(n)] = ci 46 | } 47 | } 48 | 49 | func LookupCommandInfo(commandName string) CommandInfo { 50 | if ci, ok := commandInfos[commandName]; ok { 51 | return ci 52 | } 53 | return commandInfos[strings.ToUpper(commandName)] 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/willf/bitset/README.md: -------------------------------------------------------------------------------- 1 | Package bitset implements bitsets, a mapping 2 | between non-negative integers and boolean values. It should be more 3 | efficient than map[uint] bool. 4 | 5 | It provides methods for setting, clearing, flipping, and testing 6 | individual integers. 7 | 8 | But it also provides set intersection, union, difference, 9 | complement, and symmetric operations, as well as tests to 10 | check whether any, all, or no bits are set, and querying a 11 | bitset's current length and number of postive bits. 12 | 13 | BitSets are expanded to the size of the largest set bit; the 14 | memory allocation is approximately Max bits, where Max is 15 | the largest set bit. BitSets are never shrunk. On creation, 16 | a hint can be given for the number of bits that will be used. 17 | 18 | Many of the methods, including Set, Clear, and Flip, return 19 | a BitSet pointer, which allows for chaining. 20 | 21 | Example use: 22 | 23 | import "bitset" 24 | var b BitSet 25 | b.Set(10).Set(11) 26 | if b.Test(1000) { 27 | b.Clear(1000) 28 | } 29 | for i,e := v.NextSet(0); e; i,e = v.NextSet(i + 1) { 30 | frmt.Println("The following bit is set:",i); 31 | } 32 | if B.Intersection(bitset.New(100).Set(10)).Count() > 1 { 33 | fmt.Println("Intersection works.") 34 | } 35 | 36 | As an alternative to BitSets, one should check out the 'big' package, 37 | which provides a (less set-theoretical) view of bitsets. 38 | 39 | Discussions golang-nuts Google Group: 40 | 41 | * [Revised BitSet](https://groups.google.com/forum/#!topic/golang-nuts/5i3l0CXDiBg) 42 | * [simple bitset?](https://groups.google.com/d/topic/golang-nuts/7n1VkRTlBf4/discussion) 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bloom Filters for Golang 2 | 3 | Bloom filter for go, backed by redis or in process bitset 4 | 5 | If you are not familiar with how Bloom filters work and their usefulness, 6 | [please read](https://en.wikipedia.org/wiki/Bloom_filter). 7 | 8 | [![Build Status](https://travis-ci.org/bculberson/bloom.svg?branch=master)](https://travis-ci.org/bculberson/bloom) 9 | 10 | ## Example Usage (in process): 11 | 12 | install with 13 | ``` 14 | go get gopkg.in/bculberson/bloom.v2 15 | ``` 16 | 17 | ``go 18 | import ( 19 | "gopkg.in/bculberson/bloom.v2" 20 | ) 21 | `` 22 | 23 | This bloom filter is initialized to hold 1000 keys and 24 | will have a false positive rate of 1% (.01). 25 | 26 | ```go 27 | m, k := bloom.EstimateParameters(1000, .01) 28 | b := bloom.New(m, k, bloom.NewBitSet(m)) 29 | b.Add([]byte("some key")) 30 | exists, _ := b.Exists([]byte("some key")) 31 | doesNotExist, _ := b.Exists([]byte("some other key")) 32 | ``` 33 | 34 | ## Example Usage (redis backed): 35 | 36 | This bloom filter is initialized to hold 1000 keys and 37 | will have a false positive rate of 1% (.01). 38 | 39 | This library uses [http://github.com/garyburd/redigo/redis](http://github.com/garyburd/redigo/redis) 40 | 41 | ```go 42 | pool := &redis.Pool{ 43 | MaxIdle: 3, 44 | IdleTimeout: 240 * time.Second, 45 | Dial: func() (redis.Conn, error) { return redis.Dial("tcp", addr) }, 46 | } 47 | 48 | 49 | conn := pool.Get() 50 | m, k := bloom.EstimateParameters(1000, .01) 51 | bitSet := bloom.NewRedisBitSet("test_key", m, conn) 52 | b := bloom.New(m, k, bitSet) 53 | b.Add([]byte("some key")) 54 | exists, _ := b.Exists([]byte("some key")) 55 | doesNotExist, _ := b.Exists([]byte("some other key")) 56 | ``` 57 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/redis.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | // Error represents an error returned in a command reply. 18 | type Error string 19 | 20 | func (err Error) Error() string { return string(err) } 21 | 22 | // Conn represents a connection to a Redis server. 23 | type Conn interface { 24 | // Close closes the connection. 25 | Close() error 26 | 27 | // Err returns a non-nil value if the connection is broken. The returned 28 | // value is either the first non-nil value returned from the underlying 29 | // network connection or a protocol parsing error. Applications should 30 | // close broken connections. 31 | Err() error 32 | 33 | // Do sends a command to the server and returns the received reply. 34 | Do(commandName string, args ...interface{}) (reply interface{}, err error) 35 | 36 | // Send writes the command to the client's output buffer. 37 | Send(commandName string, args ...interface{}) error 38 | 39 | // Flush flushes the output buffer to the Redis server. 40 | Flush() error 41 | 42 | // Receive receives a single reply from the Redis server 43 | Receive() (reply interface{}, err error) 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/README.md: -------------------------------------------------------------------------------- 1 | # Redeo [![Build Status](https://travis-ci.org/bsm/redeo.png?branch=master)](https://travis-ci.org/bsm/redeo) 2 | 3 | High-performance framework for building redis-protocol compatible TCP 4 | servers/services. Optimised for speed! 5 | 6 | ### Example 7 | 8 | ```go 9 | package main 10 | 11 | import ( 12 | "github.com/bsm/redeo" 13 | "log" 14 | ) 15 | 16 | func main() { 17 | srv := redeo.NewServer(&redeo.Config{Addr: "localhost:9736"}) 18 | srv.HandleFunc("ping", func(out *redeo.Responder, _ *redeo.Request) error { 19 | out.WriteInlineString("PONG") 20 | return nil 21 | }) 22 | 23 | log.Printf("Listening on tcp://%s", srv.Addr()) 24 | log.Fatal(srv.ListenAndServe()) 25 | } 26 | ``` 27 | 28 | ### Licence 29 | 30 | ``` 31 | Copyright (c) 2014 Black Square Media 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining 34 | a copy of this software and associated documentation files (the 35 | "Software"), to deal in the Software without restriction, including 36 | without limitation the rights to use, copy, modify, merge, publish, 37 | distribute, sublicense, and/or sell copies of the Software, and to 38 | permit persons to whom the Software is furnished to do so, subject to 39 | the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be 42 | included in all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 45 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 46 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 47 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 48 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 49 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 50 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 51 | ``` 52 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/info_test.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("ServerInfo", func() { 9 | var subject *ServerInfo 10 | 11 | BeforeEach(func() { 12 | a, b, c := NewClient(&mockConn{Port: 10001}), NewClient(&mockConn{Port: 10002}), NewClient(&mockConn{Port: 10004}) 13 | a.trackCommand("get") 14 | b.trackCommand("set") 15 | c.trackCommand("info") 16 | 17 | clients := newClientRegistry() 18 | clients.Put(a) 19 | clients.Put(b) 20 | clients.Put(c) 21 | 22 | subject = newServerInfo(&Config{ 23 | Addr: "127.0.0.1:9736", 24 | Socket: "/tmp/redeo.sock", 25 | }, clients) 26 | for i := 0; i < 5; i++ { 27 | subject.onConnect() 28 | } 29 | for i := 0; i < 12; i++ { 30 | subject.onCommand() 31 | } 32 | }) 33 | 34 | It("should generate info string", func() { 35 | str := subject.String() 36 | Expect(str).To(ContainSubstring("# Server\n")) 37 | Expect(str).To(MatchRegexp(`process_id:\d+\n`)) 38 | Expect(str).To(ContainSubstring("tcp_port:9736\nunix_socket:/tmp/redeo.sock\n")) 39 | Expect(str).To(MatchRegexp(`uptime_in_seconds:\d+\n`)) 40 | Expect(str).To(MatchRegexp(`uptime_in_days:\d+\n`)) 41 | 42 | Expect(str).To(ContainSubstring("# Clients\nconnected_clients:3\n")) 43 | Expect(str).To(ContainSubstring("# Stats\ntotal_connections_received:5\ntotal_commands_processed:12\n")) 44 | }) 45 | 46 | It("should retrieve a list of clients", func() { 47 | Expect(subject.Clients()).To(HaveLen(3)) 48 | }) 49 | 50 | It("should generate client string", func() { 51 | str := subject.ClientsString() 52 | Expect(str).To(MatchRegexp(`id=\d+ addr=1\.2\.3\.4\:10001 age=\d+ idle=\d+ cmd=get`)) 53 | Expect(str).To(MatchRegexp(`id=\d+ addr=1\.2\.3\.4\:10002 age=\d+ idle=\d+ cmd=set`)) 54 | Expect(str).To(MatchRegexp(`id=\d+ addr=1\.2\.3\.4\:10004 age=\d+ idle=\d+ cmd=info`)) 55 | }) 56 | 57 | }) 58 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/redistest/testdb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Package redistest contains utilities for writing Redigo tests. 16 | package redistest 17 | 18 | import ( 19 | "errors" 20 | "time" 21 | 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | type testConn struct { 26 | redis.Conn 27 | } 28 | 29 | func (t testConn) Close() error { 30 | _, err := t.Conn.Do("SELECT", "9") 31 | if err != nil { 32 | return nil 33 | } 34 | _, err = t.Conn.Do("FLUSHDB") 35 | if err != nil { 36 | return err 37 | } 38 | return t.Conn.Close() 39 | } 40 | 41 | // Dial dials the local Redis server and selects database 9. To prevent 42 | // stomping on real data, DialTestDB fails if database 9 contains data. The 43 | // returned connection flushes database 9 on close. 44 | func Dial() (redis.Conn, error) { 45 | c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | _, err = c.Do("SELECT", "9") 51 | if err != nil { 52 | c.Close() 53 | return nil, err 54 | } 55 | 56 | n, err := redis.Int(c.Do("DBSIZE")) 57 | if err != nil { 58 | c.Close() 59 | return nil, err 60 | } 61 | 62 | if n != 0 { 63 | c.Close() 64 | return nil, errors.New("database #9 is not empty, test can not continue") 65 | } 66 | 67 | return testConn{c}, nil 68 | } 69 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/sorted_set_test.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSortedSetImpl(t *testing.T) { 8 | s := newSortedSet() 9 | equals(t, 0, s.card()) 10 | s.set(3.1415, "pi") 11 | s.set(2*3.1415, "2pi") 12 | s.set(3*3.1415, "3pi") 13 | equals(t, 3, s.card()) 14 | // replace works? 15 | s.set(3.141592, "pi") 16 | equals(t, 3, s.card()) 17 | 18 | // Get a key 19 | { 20 | pi, ok := s.get("pi") 21 | assert(t, ok, "got pi") 22 | equals(t, 3.141592, pi) 23 | } 24 | 25 | // Set ordered by score 26 | { 27 | elems := s.byScore(asc) 28 | equals(t, 3, len(elems)) 29 | equals(t, ssElems{ 30 | {3.141592, "pi"}, 31 | {2 * 3.1415, "2pi"}, 32 | {3 * 3.1415, "3pi"}, 33 | }, elems) 34 | } 35 | 36 | // Rank of a key 37 | { 38 | rank, found := s.rankByScore("pi", asc) 39 | assert(t, found, "Found pi") 40 | equals(t, 0, rank) 41 | 42 | rank, found = s.rankByScore("3pi", desc) 43 | assert(t, found, "Found 3pi") 44 | equals(t, 0, rank) 45 | 46 | rank, found = s.rankByScore("3pi", asc) 47 | assert(t, found, "Found 3pi") 48 | equals(t, 2, rank) 49 | 50 | _, found = s.rankByScore("nosuch", asc) 51 | assert(t, !found, "Did not find nosuch") 52 | } 53 | } 54 | 55 | func TestSortOrder(t *testing.T) { 56 | // Keys with the same key should be sorted lexicographically 57 | s := newSortedSet() 58 | equals(t, 0, s.card()) 59 | s.set(1, "one") 60 | s.set(1, "1") 61 | s.set(1, "eins") 62 | s.set(2, "two") 63 | s.set(2, "2") 64 | s.set(2, "zwei") 65 | s.set(3, "three") 66 | s.set(3, "3") 67 | s.set(3, "drei") 68 | equals(t, 9, s.card()) 69 | 70 | // Set ordered by score, member 71 | { 72 | elems := s.byScore(asc) 73 | equals(t, 9, len(elems)) 74 | equals(t, ssElems{ 75 | {1, "1"}, 76 | {1, "eins"}, 77 | {1, "one"}, 78 | {2, "2"}, 79 | {2, "two"}, 80 | {2, "zwei"}, 81 | {3, "3"}, 82 | {3, "drei"}, 83 | {3, "three"}, 84 | }, elems) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/sorted_set.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | // The most KISS way to implement a sorted set. Luckily we don't care about 4 | // performance that much. 5 | 6 | import ( 7 | "sort" 8 | ) 9 | 10 | type direction int 11 | 12 | const ( 13 | asc direction = iota 14 | desc 15 | ) 16 | 17 | type sortedSet map[string]float64 18 | 19 | type ssElem struct { 20 | score float64 21 | member string 22 | } 23 | type ssElems []ssElem 24 | 25 | type byScore ssElems 26 | 27 | func (sse byScore) Len() int { return len(sse) } 28 | func (sse byScore) Swap(i, j int) { sse[i], sse[j] = sse[j], sse[i] } 29 | func (sse byScore) Less(i, j int) bool { 30 | if sse[i].score != sse[j].score { 31 | return sse[i].score < sse[j].score 32 | } 33 | return sse[i].member < sse[j].member 34 | } 35 | 36 | func newSortedSet() sortedSet { 37 | return sortedSet{} 38 | } 39 | 40 | func (ss *sortedSet) card() int { 41 | return len(*ss) 42 | } 43 | 44 | func (ss *sortedSet) set(score float64, member string) { 45 | (*ss)[member] = score 46 | } 47 | 48 | func (ss *sortedSet) get(member string) (float64, bool) { 49 | v, ok := (*ss)[member] 50 | return v, ok 51 | } 52 | 53 | // elems gives the list of ssElem, ready to sort. 54 | func (ss *sortedSet) elems() ssElems { 55 | elems := make(ssElems, 0, len(*ss)) 56 | for e, s := range *ss { 57 | elems = append(elems, ssElem{s, e}) 58 | } 59 | return elems 60 | } 61 | 62 | func (ss *sortedSet) byScore(d direction) ssElems { 63 | elems := ss.elems() 64 | sort.Sort(byScore(elems)) 65 | if d == desc { 66 | reverseElems(elems) 67 | } 68 | return ssElems(elems) 69 | } 70 | 71 | // rankByScore gives the (0-based) index of member, or returns false. 72 | func (ss *sortedSet) rankByScore(member string, d direction) (int, bool) { 73 | if _, ok := (*ss)[member]; !ok { 74 | return 0, false 75 | } 76 | for i, e := range ss.byScore(d) { 77 | if e.member == member { 78 | return i, true 79 | } 80 | } 81 | // Can't happen 82 | return 0, false 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/info/info.go: -------------------------------------------------------------------------------- 1 | package info 2 | 3 | // Main info registry. 4 | // Please note: in order to minimise performance impact info registries 5 | // are not using locks are therefore not thread-safe. Please make sure 6 | // you register all metrics and values before you start the server. 7 | type Registry struct{ sections []*Section } 8 | 9 | // New creates a new Registry 10 | func New() *Registry { 11 | return &Registry{make([]*Section, 0)} 12 | } 13 | 14 | // Section returns a section, or appends a new one 15 | // when the given name cannot be found 16 | func (r *Registry) Section(name string) *Section { 17 | for _, s := range r.sections { 18 | if s.name == name { 19 | return s 20 | } 21 | } 22 | section := &Section{name: name, kvs: make([]kv, 0)} 23 | r.sections = append(r.sections, section) 24 | return section 25 | } 26 | 27 | // Clear removes all sections from the registry 28 | func (r *Registry) Clear() { 29 | r.sections = r.sections[:0] 30 | } 31 | 32 | // String generates an info string output 33 | func (r *Registry) String() string { 34 | result := "" 35 | for _, section := range r.sections { 36 | if len(section.kvs) > 0 { 37 | result += "# " + section.name + "\n" + section.String() + "\n" 38 | } 39 | } 40 | if len(result) > 1 { 41 | result = result[:len(result)-1] 42 | } 43 | return result 44 | } 45 | 46 | // An info section contains multiple values 47 | type Section struct { 48 | name string 49 | kvs []kv 50 | } 51 | 52 | // Register registers a value under a name 53 | func (s *Section) Register(name string, value Value) { 54 | s.kvs = append(s.kvs, kv{name, value}) 55 | } 56 | 57 | // Clear removes all values from a section 58 | func (s *Section) Clear() { 59 | s.kvs = s.kvs[:0] 60 | } 61 | 62 | // String generates a section string output 63 | func (s *Section) String() string { 64 | result := "" 65 | for _, kv := range s.kvs { 66 | result += kv.name + ":" + kv.value.String() + "\n" 67 | } 68 | return result 69 | } 70 | 71 | type kv struct { 72 | name string 73 | value Value 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/check.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | // 'Fail' methods. 4 | 5 | import ( 6 | "fmt" 7 | "path/filepath" 8 | "reflect" 9 | "runtime" 10 | "sort" 11 | ) 12 | 13 | // T is implemented by Testing.T 14 | type T interface { 15 | Fail() 16 | } 17 | 18 | // CheckGet does not call Errorf() iff there is a string key with the 19 | // expected value. Normal use case is `m.CheckGet(t, "username", "theking")`. 20 | func (m *Miniredis) CheckGet(t T, key, expected string) { 21 | found, err := m.Get(key) 22 | if err != nil { 23 | lError(t, "GET error, key %#v: %v", key, err) 24 | return 25 | } 26 | if found != expected { 27 | lError(t, "GET error, key %#v: Expected %#v, got %#v", key, expected, found) 28 | return 29 | } 30 | } 31 | 32 | // CheckList does not call Errorf() iff there is a list key with the 33 | // expected values. 34 | // Normal use case is `m.CheckGet(t, "favorite_colors", "red", "green", "infrared")`. 35 | func (m *Miniredis) CheckList(t T, key string, expected ...string) { 36 | found, err := m.List(key) 37 | if err != nil { 38 | lError(t, "List error, key %#v: %v", key, err) 39 | return 40 | } 41 | if !reflect.DeepEqual(expected, found) { 42 | lError(t, "List error, key %#v: Expected %#v, got %#v", key, expected, found) 43 | return 44 | } 45 | } 46 | 47 | // CheckSet does not call Errorf() iff there is a set key with the 48 | // expected values. 49 | // Normal use case is `m.CheckSet(t, "visited", "Rome", "Stockholm", "Dublin")`. 50 | func (m *Miniredis) CheckSet(t T, key string, expected ...string) { 51 | found, err := m.Members(key) 52 | if err != nil { 53 | lError(t, "Set error, key %#v: %v", key, err) 54 | return 55 | } 56 | sort.Strings(expected) 57 | if !reflect.DeepEqual(expected, found) { 58 | lError(t, "Set error, key %#v: Expected %#v, got %#v", key, expected, found) 59 | return 60 | } 61 | } 62 | 63 | func lError(t T, format string, args ...interface{}) { 64 | _, file, line, _ := runtime.Caller(2) 65 | prefix := fmt.Sprintf("%s:%d: ", filepath.Base(file), line) 66 | fmt.Printf(prefix+format+"\n", args...) 67 | t.Fail() 68 | } 69 | -------------------------------------------------------------------------------- /redis_bitset.go: -------------------------------------------------------------------------------- 1 | package bloom 2 | 3 | import ( 4 | "fmt" 5 | "github.com/garyburd/redigo/redis" 6 | "strings" 7 | ) 8 | 9 | const redisMaxLength = 8 * 512 * 1024 * 1024 10 | 11 | type Connection interface { 12 | Do(cmd string, args ...interface{}) (reply interface{}, err error) 13 | Send(cmd string, args ...interface{}) error 14 | Flush() error 15 | } 16 | 17 | type RedisBitSet struct { 18 | keyPrefix string 19 | conn Connection 20 | m uint 21 | } 22 | 23 | func NewRedisBitSet(keyPrefix string, m uint, conn Connection) *RedisBitSet { 24 | return &RedisBitSet{keyPrefix, conn, m} 25 | } 26 | 27 | func (r *RedisBitSet) Set(offsets []uint) error { 28 | for _, offset := range offsets { 29 | key, thisOffset := r.getKeyOffset(offset) 30 | err := r.conn.Send("SETBIT", key, thisOffset, 1) 31 | if err != nil { 32 | return err 33 | } 34 | } 35 | 36 | return r.conn.Flush() 37 | } 38 | 39 | func (r *RedisBitSet) Test(offsets []uint) (bool, error) { 40 | for _, offset := range offsets { 41 | key, thisOffset := r.getKeyOffset(offset) 42 | bitValue, err := redis.Int(r.conn.Do("GETBIT", key, thisOffset)) 43 | if err != nil { 44 | return false, err 45 | } 46 | if bitValue == 0 { 47 | return false, nil 48 | } 49 | } 50 | 51 | return true, nil 52 | } 53 | 54 | func (r *RedisBitSet) Expire(seconds uint) error { 55 | n := uint(0) 56 | for n <= uint(r.m/redisMaxLength) { 57 | key := fmt.Sprintf("%s:%d", r.keyPrefix, n) 58 | n = n + 1 59 | err := r.conn.Send("EXPIRE", key, seconds) 60 | if err != nil { 61 | return err 62 | } 63 | } 64 | return r.conn.Flush() 65 | } 66 | 67 | func (r *RedisBitSet) Delete() error { 68 | n := uint(0) 69 | keys := make([]string, 0) 70 | for n <= uint(r.m/redisMaxLength) { 71 | key := fmt.Sprintf("%s:%d", r.keyPrefix, n) 72 | keys = append(keys, key) 73 | n = n + 1 74 | } 75 | _, err := r.conn.Do("DEL", strings.Join(keys, " ")) 76 | return err 77 | } 78 | 79 | func (r *RedisBitSet) getKeyOffset(offset uint) (string, uint) { 80 | n := uint(offset / redisMaxLength) 81 | thisOffset := offset - n*redisMaxLength 82 | key := fmt.Sprintf("%s:%d", r.keyPrefix, n) 83 | return key, thisOffset 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/client.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | ) 10 | 11 | type clientSlice []*Client 12 | 13 | func (p clientSlice) Len() int { return len(p) } 14 | func (p clientSlice) Less(i, j int) bool { return p[i].id < p[j].id } 15 | func (p clientSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 16 | 17 | var clientInc = uint64(0) 18 | 19 | // A client is the origin of a request 20 | type Client struct { 21 | Ctx interface{} 22 | 23 | id uint64 24 | conn net.Conn 25 | 26 | firstAccess time.Time 27 | lastAccess time.Time 28 | lastCommand string 29 | 30 | quit bool 31 | mutex sync.Mutex 32 | } 33 | 34 | // NewClient creates a new client info container 35 | func NewClient(conn net.Conn) *Client { 36 | now := time.Now() 37 | return &Client{ 38 | id: atomic.AddUint64(&clientInc, 1), 39 | conn: conn, 40 | firstAccess: now, 41 | lastAccess: now, 42 | } 43 | } 44 | 45 | // ID return the unique client id 46 | func (i *Client) ID() uint64 { return i.id } 47 | 48 | // RemoteAddr return the remote client address 49 | func (i *Client) RemoteAddr() net.Addr { return i.conn.RemoteAddr() } 50 | 51 | // Close will disconnect as soon as all pending replies have been written 52 | // to the client 53 | func (i *Client) Close() { i.quit = true } 54 | 55 | // String generates an info string 56 | func (i *Client) String() string { 57 | i.mutex.Lock() 58 | cmd := i.lastCommand 59 | atime := i.lastAccess 60 | i.mutex.Unlock() 61 | 62 | now := time.Now() 63 | age := now.Sub(i.firstAccess) / time.Second 64 | idle := now.Sub(atime) / time.Second 65 | 66 | return fmt.Sprintf("id=%d addr=%s age=%d idle=%d cmd=%s", i.id, i.RemoteAddr(), age, idle, cmd) 67 | } 68 | 69 | // ------------------------------------------------------------------------ 70 | 71 | // Instantly closes the underlying socket connection 72 | func (i *Client) close() error { return i.conn.Close() } 73 | 74 | // Tracks user command 75 | func (i *Client) trackCommand(cmd string) { 76 | i.mutex.Lock() 77 | defer i.mutex.Unlock() 78 | 79 | i.lastAccess = time.Now() 80 | i.lastCommand = cmd 81 | } 82 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/request.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // Request contains a command, arguments, and client information 11 | type Request struct { 12 | Name string `json:"name"` 13 | Args []string `json:"args,omitempty"` 14 | Ctx interface{} `json:"ctx,omitempty"` 15 | 16 | client *Client 17 | } 18 | 19 | // Client returns the client 20 | func (r *Request) Client() *Client { 21 | return r.client 22 | } 23 | 24 | // WrongNumberOfArgs generates a standard client error 25 | func (r *Request) WrongNumberOfArgs() ClientError { 26 | return WrongNumberOfArgs(r.Name) 27 | } 28 | 29 | // UnknownCommand generates a standard client error 30 | func (r *Request) UnknownCommand() ClientError { 31 | return UnknownCommand(r.Name) 32 | } 33 | 34 | // ParseRequest parses a new request from a buffered connection 35 | func ParseRequest(rd *bufio.Reader) (*Request, error) { 36 | line, err := rd.ReadString('\n') 37 | if err != nil || len(line) < 3 { 38 | return nil, io.EOF 39 | } 40 | 41 | // Truncate CRLF 42 | line = line[:len(line)-2] 43 | 44 | // Return if inline 45 | if line[0] != codeBulkLen { 46 | return &Request{Name: strings.ToLower(line)}, nil 47 | } 48 | 49 | argc, err := strconv.Atoi(line[1:]) 50 | if err != nil { 51 | return nil, ErrInvalidRequest 52 | } 53 | 54 | args := make([]string, argc) 55 | for i := 0; i < argc; i++ { 56 | if args[i], err = parseArgument(rd); err != nil { 57 | return nil, err 58 | } 59 | } 60 | return &Request{Name: strings.ToLower(args[0]), Args: args[1:]}, nil 61 | } 62 | 63 | func parseArgument(rd *bufio.Reader) (string, error) { 64 | line, err := rd.ReadString('\n') 65 | if err != nil { 66 | return "", io.EOF 67 | } else if len(line) < 3 { 68 | return "", io.EOF 69 | } else if line[0] != codeStrLen { 70 | return "", ErrInvalidRequest 71 | } 72 | 73 | blen, err := strconv.Atoi(line[1 : len(line)-2]) 74 | if err != nil { 75 | return "", ErrInvalidRequest 76 | } 77 | 78 | buf := make([]byte, blen+2) 79 | if _, err := io.ReadFull(rd, buf); err != nil { 80 | return "", io.EOF 81 | } 82 | 83 | return string(buf[:blen]), nil 84 | } 85 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/cmd_connection_test.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/garyburd/redigo/redis" 7 | ) 8 | 9 | func TestAuth(t *testing.T) { 10 | s, err := Run() 11 | ok(t, err) 12 | defer s.Close() 13 | c, err := redis.Dial("tcp", s.Addr()) 14 | ok(t, err) 15 | 16 | _, err = c.Do("AUTH", "foo", "bar") 17 | assert(t, err != nil, "no password set") 18 | 19 | s.RequireAuth("nocomment") 20 | _, err = c.Do("PING", "foo", "bar") 21 | assert(t, err != nil, "need AUTH") 22 | 23 | _, err = c.Do("AUTH", "wrongpasswd") 24 | assert(t, err != nil, "wrong password") 25 | 26 | _, err = c.Do("AUTH", "nocomment") 27 | ok(t, err) 28 | 29 | _, err = c.Do("PING") 30 | ok(t, err) 31 | } 32 | 33 | func TestEcho(t *testing.T) { 34 | s, err := Run() 35 | ok(t, err) 36 | defer s.Close() 37 | c, err := redis.Dial("tcp", s.Addr()) 38 | ok(t, err) 39 | 40 | r, err := redis.String(c.Do("ECHO", "hello\nworld")) 41 | ok(t, err) 42 | equals(t, "hello\nworld", r) 43 | } 44 | 45 | func TestSelect(t *testing.T) { 46 | s, err := Run() 47 | ok(t, err) 48 | defer s.Close() 49 | c, err := redis.Dial("tcp", s.Addr()) 50 | ok(t, err) 51 | 52 | _, err = redis.String(c.Do("SET", "foo", "bar")) 53 | ok(t, err) 54 | 55 | _, err = redis.String(c.Do("SELECT", "5")) 56 | ok(t, err) 57 | 58 | _, err = redis.String(c.Do("SET", "foo", "baz")) 59 | ok(t, err) 60 | 61 | // Direct access. 62 | got, err := s.Get("foo") 63 | ok(t, err) 64 | equals(t, "bar", got) 65 | s.Select(5) 66 | got, err = s.Get("foo") 67 | ok(t, err) 68 | equals(t, "baz", got) 69 | 70 | // Another connection should have its own idea of the db: 71 | c2, err := redis.Dial("tcp", s.Addr()) 72 | ok(t, err) 73 | v, err := redis.String(c2.Do("GET", "foo")) 74 | ok(t, err) 75 | equals(t, "bar", v) 76 | } 77 | 78 | func TestQuit(t *testing.T) { 79 | s, err := Run() 80 | ok(t, err) 81 | defer s.Close() 82 | c, err := redis.Dial("tcp", s.Addr()) 83 | ok(t, err) 84 | 85 | v, err := redis.String(c.Do("QUIT")) 86 | ok(t, err) 87 | equals(t, "OK", v) 88 | 89 | v, err = redis.String(c.Do("PING")) 90 | assert(t, err != nil, "QUIT closed the client") 91 | equals(t, "", v) 92 | } 93 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/README.markdown: -------------------------------------------------------------------------------- 1 | Redigo 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/garyburd/redigo.svg?branch=master)](https://travis-ci.org/garyburd/redigo) 5 | [![GoDoc](https://godoc.org/github.com/garyburd/redigo/redis?status.svg)](https://godoc.org/github.com/garyburd/redigo/redis) 6 | 7 | Redigo is a [Go](http://golang.org/) client for the [Redis](http://redis.io/) database. 8 | 9 | Features 10 | ------- 11 | 12 | * A [Print-like](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Executing_Commands) API with support for all Redis commands. 13 | * [Pipelining](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining), including pipelined transactions. 14 | * [Publish/Subscribe](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Publish_and_Subscribe). 15 | * [Connection pooling](http://godoc.org/github.com/garyburd/redigo/redis#Pool). 16 | * [Script helper type](http://godoc.org/github.com/garyburd/redigo/redis#Script) with optimistic use of EVALSHA. 17 | * [Helper functions](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Reply_Helpers) for working with command replies. 18 | 19 | Documentation 20 | ------------- 21 | 22 | - [API Reference](http://godoc.org/github.com/garyburd/redigo/redis) 23 | - [FAQ](https://github.com/garyburd/redigo/wiki/FAQ) 24 | 25 | Installation 26 | ------------ 27 | 28 | Install Redigo using the "go get" command: 29 | 30 | go get github.com/garyburd/redigo/redis 31 | 32 | The Go distribution is Redigo's only dependency. 33 | 34 | Related Projects 35 | ---------------- 36 | 37 | - [rafaeljusto/redigomock](https://godoc.org/github.com/rafaeljusto/redigomock) - A mock library for Redigo. 38 | - [chasex/redis-go-cluster](https://github.com/chasex/redis-go-cluster) - A Redis cluster client implementation. 39 | - [FZambia/go-sentinel](https://github.com/FZambia/go-sentinel) - Redis Sentinel support for Redigo 40 | - [PuerkitoBio/redisc](https://github.com/PuerkitoBio/redisc) - Redis Cluster client built on top of Redigo 41 | 42 | Contributing 43 | ------------ 44 | 45 | Send email to Gary Burd (address in GitHub profile) before doing any work on Redigo. 46 | 47 | License 48 | ------- 49 | 50 | Redigo is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). 51 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/cmd_connection.go: -------------------------------------------------------------------------------- 1 | // Commands from http://redis.io/commands#connection 2 | 3 | package miniredis 4 | 5 | import ( 6 | "strconv" 7 | 8 | "github.com/bsm/redeo" 9 | ) 10 | 11 | func commandsConnection(m *Miniredis, srv *redeo.Server) { 12 | srv.HandleFunc("AUTH", m.cmdAuth) 13 | srv.HandleFunc("ECHO", m.cmdEcho) 14 | srv.HandleFunc("PING", m.cmdPing) 15 | srv.HandleFunc("SELECT", m.cmdSelect) 16 | srv.HandleFunc("QUIT", m.cmdQuit) 17 | } 18 | 19 | // PING 20 | func (m *Miniredis) cmdPing(out *redeo.Responder, r *redeo.Request) error { 21 | if !m.handleAuth(r.Client(), out) { 22 | return nil 23 | } 24 | out.WriteInlineString("PONG") 25 | return nil 26 | } 27 | 28 | // AUTH 29 | func (m *Miniredis) cmdAuth(out *redeo.Responder, r *redeo.Request) error { 30 | if len(r.Args) != 1 { 31 | setDirty(r.Client()) 32 | return r.WrongNumberOfArgs() 33 | } 34 | pw := r.Args[0] 35 | 36 | m.Lock() 37 | defer m.Unlock() 38 | if m.password == "" { 39 | out.WriteErrorString("ERR Client sent AUTH, but no password is set") 40 | return nil 41 | } 42 | if m.password != pw { 43 | out.WriteErrorString("ERR invalid password") 44 | return nil 45 | } 46 | 47 | setAuthenticated(r.Client()) 48 | out.WriteOK() 49 | return nil 50 | } 51 | 52 | // ECHO 53 | func (m *Miniredis) cmdEcho(out *redeo.Responder, r *redeo.Request) error { 54 | if len(r.Args) != 1 { 55 | setDirty(r.Client()) 56 | return r.WrongNumberOfArgs() 57 | } 58 | if !m.handleAuth(r.Client(), out) { 59 | return nil 60 | } 61 | msg := r.Args[0] 62 | out.WriteString(msg) 63 | return nil 64 | } 65 | 66 | // SELECT 67 | func (m *Miniredis) cmdSelect(out *redeo.Responder, r *redeo.Request) error { 68 | if len(r.Args) != 1 { 69 | setDirty(r.Client()) 70 | return r.WrongNumberOfArgs() 71 | } 72 | if !m.handleAuth(r.Client(), out) { 73 | return nil 74 | } 75 | 76 | id, err := strconv.Atoi(r.Args[0]) 77 | if err != nil { 78 | id = 0 79 | } 80 | 81 | m.Lock() 82 | defer m.Unlock() 83 | 84 | ctx := getCtx(r.Client()) 85 | ctx.selectedDB = id 86 | 87 | out.WriteOK() 88 | return nil 89 | } 90 | 91 | // QUIT 92 | func (m *Miniredis) cmdQuit(out *redeo.Responder, r *redeo.Request) error { 93 | // QUIT isn't transactionfied and accepts any arguments. 94 | out.WriteOK() 95 | r.Client().Close() 96 | return nil 97 | } 98 | -------------------------------------------------------------------------------- /bloom_test.go: -------------------------------------------------------------------------------- 1 | package bloom_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "encoding/binary" 8 | "github.com/alicebob/miniredis" 9 | "github.com/bculberson/bloom" 10 | "github.com/garyburd/redigo/redis" 11 | ) 12 | 13 | func TestRedisBloomFilter(t *testing.T) { 14 | s, err := miniredis.Run() 15 | if err != nil { 16 | t.Error("Miniredis could not start") 17 | } 18 | defer s.Close() 19 | 20 | pool := &redis.Pool{ 21 | MaxIdle: 3, 22 | IdleTimeout: 240 * time.Second, 23 | Dial: func() (redis.Conn, error) { return redis.Dial("tcp", s.Addr()) }, 24 | } 25 | conn := pool.Get() 26 | defer conn.Close() 27 | 28 | m, k := bloom.EstimateParameters(1000, .01) 29 | bitSet := bloom.NewRedisBitSet("test_key", m, conn) 30 | b := bloom.New(m, k, bitSet) 31 | testBloomFilter(t, b) 32 | } 33 | 34 | func TestBloomFilter(t *testing.T) { 35 | m, k := bloom.EstimateParameters(1000, .01) 36 | b := bloom.New(m, k, bloom.NewBitSet(m)) 37 | testBloomFilter(t, b) 38 | } 39 | 40 | func TestCollision(t *testing.T) { 41 | n := uint(10000) 42 | fp := .01 43 | m, k := bloom.EstimateParameters(n, fp) 44 | b := bloom.New(m, k, bloom.NewBitSet(m)) 45 | shouldNotExist := 0 46 | for i := uint(0); i < n; i++ { 47 | data := make([]byte, 4) 48 | binary.LittleEndian.PutUint32(data, uint32(i)) 49 | existsBefore, err := b.Exists(data) 50 | if err != nil { 51 | t.Fatal("Error checking existence.") 52 | } 53 | if existsBefore { 54 | shouldNotExist = shouldNotExist + 1 55 | } 56 | err = b.Add(data) 57 | if err != nil { 58 | t.Fatal("Error adding item.") 59 | } 60 | existsAfter, err := b.Exists(data) 61 | if err != nil { 62 | t.Fatal("Error checking existence.") 63 | } 64 | if !existsAfter { 65 | t.Fatal("Item should exist.") 66 | } 67 | } 68 | if float64(shouldNotExist) > fp*float64(n) { 69 | t.Fatal("Too many false positives.") 70 | } 71 | } 72 | 73 | func testBloomFilter(t *testing.T, b *bloom.BloomFilter) { 74 | data := []byte("some key") 75 | existsBefore, err := b.Exists(data) 76 | if err != nil { 77 | t.Fatal("Error checking for existence in bloom filter") 78 | } 79 | if existsBefore { 80 | t.Fatal("Bloom filter should not contain this data") 81 | } 82 | err = b.Add(data) 83 | if err != nil { 84 | t.Fatal("Error adding to bloom filter") 85 | } 86 | existsAfter, err := b.Exists(data) 87 | if err != nil { 88 | t.Fatal("Error checking for existence in bloom filter") 89 | } 90 | if !existsAfter { 91 | t.Fatal("Bloom filter should contain this data") 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/keys_test.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestKeysSel(t *testing.T) { 8 | // Helper to test the selection behind KEYS 9 | // pattern -> cases -> should match? 10 | for pat, chk := range map[string]map[string]bool{ 11 | "aap": { 12 | "aap": true, 13 | "aapnoot": false, 14 | "nootaap": false, 15 | "nootaapnoot": false, 16 | "AAP": false, 17 | }, 18 | "aap*": { 19 | "aap": true, 20 | "aapnoot": true, 21 | "nootaap": false, 22 | "nootaapnoot": false, 23 | "AAP": false, 24 | }, 25 | // No problem with regexp meta chars? 26 | "(?:a)ap*": { 27 | "(?:a)ap!": true, 28 | "aap": false, 29 | }, 30 | "*aap*": { 31 | "aap": true, 32 | "aapnoot": true, 33 | "nootaap": true, 34 | "nootaapnoot": true, 35 | "AAP": false, 36 | "a_a_p": false, 37 | }, 38 | `\*aap*`: { 39 | "*aap": true, 40 | "aap": false, 41 | "*aapnoot": true, 42 | "aapnoot": false, 43 | }, 44 | `aa?`: { 45 | "aap": true, 46 | "aal": true, 47 | "aaf": true, 48 | "aa?": true, 49 | "aap!": false, 50 | }, 51 | `aa\?`: { 52 | "aap": false, 53 | "aa?": true, 54 | "aa?!": false, 55 | }, 56 | "aa[pl]": { 57 | "aap": true, 58 | "aal": true, 59 | "aaf": false, 60 | "aa?": false, 61 | "aap!": false, 62 | }, 63 | "[ab]a[pl]": { 64 | "aap": true, 65 | "aal": true, 66 | "bap": true, 67 | "bal": true, 68 | "aaf": false, 69 | "cap": false, 70 | "aa?": false, 71 | "aap!": false, 72 | }, 73 | `\[ab\]`: { 74 | "[ab]": true, 75 | "a": false, 76 | }, 77 | `[\[ab]`: { 78 | "[": true, 79 | "a": true, 80 | "b": true, 81 | "c": false, 82 | "]": false, 83 | }, 84 | `[\[\]]`: { 85 | "[": true, 86 | "]": true, 87 | "c": false, 88 | }, 89 | `\\ap`: { 90 | `\ap`: true, 91 | `\\ap`: false, 92 | }, 93 | // Escape a normal char 94 | `\foo`: { 95 | `foo`: true, 96 | `\foo`: false, 97 | }, 98 | } { 99 | patRe := patternRE(pat) 100 | if patRe == nil { 101 | t.Errorf("'%v' won't match anything. Didn't expect that.\n", pat) 102 | continue 103 | } 104 | for key, expected := range chk { 105 | match := patRe.MatchString(key) 106 | if expected != match { 107 | t.Errorf("'%v' -> '%v'. Matches %v, should %v\n", pat, key, match, expected) 108 | } 109 | } 110 | } 111 | 112 | // Patterns which won't match anything. 113 | for _, pat := range []string{ 114 | `ap[\`, // trailing \ in char class 115 | `ap[`, // open char class 116 | `[]ap`, // empty char class 117 | `ap\`, // trailing \ 118 | } { 119 | if patternRE(pat) != nil { 120 | t.Errorf("'%v' will match something. Didn't expect that.\n", pat) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/zpop_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "github.com/garyburd/redigo/redis" 20 | ) 21 | 22 | // zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands. 23 | func zpop(c redis.Conn, key string) (result string, err error) { 24 | 25 | defer func() { 26 | // Return connection to normal state on error. 27 | if err != nil { 28 | c.Do("DISCARD") 29 | } 30 | }() 31 | 32 | // Loop until transaction is successful. 33 | for { 34 | if _, err := c.Do("WATCH", key); err != nil { 35 | return "", err 36 | } 37 | 38 | members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0)) 39 | if err != nil { 40 | return "", err 41 | } 42 | if len(members) != 1 { 43 | return "", redis.ErrNil 44 | } 45 | 46 | c.Send("MULTI") 47 | c.Send("ZREM", key, members[0]) 48 | queued, err := c.Do("EXEC") 49 | if err != nil { 50 | return "", err 51 | } 52 | 53 | if queued != nil { 54 | result = members[0] 55 | break 56 | } 57 | } 58 | 59 | return result, nil 60 | } 61 | 62 | // zpopScript pops a value from a ZSET. 63 | var zpopScript = redis.NewScript(1, ` 64 | local r = redis.call('ZRANGE', KEYS[1], 0, 0) 65 | if r ~= nil then 66 | r = r[1] 67 | redis.call('ZREM', KEYS[1], r) 68 | end 69 | return r 70 | `) 71 | 72 | // This example implements ZPOP as described at 73 | // http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting. 74 | func Example_zpop() { 75 | c, err := dial() 76 | if err != nil { 77 | fmt.Println(err) 78 | return 79 | } 80 | defer c.Close() 81 | 82 | // Add test data using a pipeline. 83 | 84 | for i, member := range []string{"red", "blue", "green"} { 85 | c.Send("ZADD", "zset", i, member) 86 | } 87 | if _, err := c.Do(""); err != nil { 88 | fmt.Println(err) 89 | return 90 | } 91 | 92 | // Pop using WATCH/MULTI/EXEC 93 | 94 | v, err := zpop(c, "zset") 95 | if err != nil { 96 | fmt.Println(err) 97 | return 98 | } 99 | fmt.Println(v) 100 | 101 | // Pop using a script. 102 | 103 | v, err = redis.String(zpopScript.Do(c, "zset")) 104 | if err != nil { 105 | fmt.Println(err) 106 | return 107 | } 108 | fmt.Println(v) 109 | 110 | // Output: 111 | // red 112 | // blue 113 | } 114 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/script_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | "time" 22 | 23 | "github.com/garyburd/redigo/redis" 24 | ) 25 | 26 | var ( 27 | // These variables are declared at package level to remove distracting 28 | // details from the examples. 29 | c redis.Conn 30 | reply interface{} 31 | err error 32 | ) 33 | 34 | func ExampleScript() { 35 | // Initialize a package-level variable with a script. 36 | var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`) 37 | 38 | // In a function, use the script Do method to evaluate the script. The Do 39 | // method optimistically uses the EVALSHA command. If the script is not 40 | // loaded, then the Do method falls back to the EVAL command. 41 | reply, err = getScript.Do(c, "foo") 42 | } 43 | 44 | func TestScript(t *testing.T) { 45 | c, err := redis.DialDefaultServer() 46 | if err != nil { 47 | t.Fatalf("error connection to database, %v", err) 48 | } 49 | defer c.Close() 50 | 51 | // To test fall back in Do, we make script unique by adding comment with current time. 52 | script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano()) 53 | s := redis.NewScript(2, script) 54 | reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")} 55 | 56 | v, err := s.Do(c, "key1", "key2", "arg1", "arg2") 57 | if err != nil { 58 | t.Errorf("s.Do(c, ...) returned %v", err) 59 | } 60 | 61 | if !reflect.DeepEqual(v, reply) { 62 | t.Errorf("s.Do(c, ..); = %v, want %v", v, reply) 63 | } 64 | 65 | err = s.Load(c) 66 | if err != nil { 67 | t.Errorf("s.Load(c) returned %v", err) 68 | } 69 | 70 | err = s.SendHash(c, "key1", "key2", "arg1", "arg2") 71 | if err != nil { 72 | t.Errorf("s.SendHash(c, ...) returned %v", err) 73 | } 74 | 75 | err = c.Flush() 76 | if err != nil { 77 | t.Errorf("c.Flush() returned %v", err) 78 | } 79 | 80 | v, err = c.Receive() 81 | if !reflect.DeepEqual(v, reply) { 82 | t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply) 83 | } 84 | 85 | err = s.Send(c, "key1", "key2", "arg1", "arg2") 86 | if err != nil { 87 | t.Errorf("s.Send(c, ...) returned %v", err) 88 | } 89 | 90 | err = c.Flush() 91 | if err != nil { 92 | t.Errorf("c.Flush() returned %v", err) 93 | } 94 | 95 | v, err = c.Receive() 96 | if !reflect.DeepEqual(v, reply) { 97 | t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply) 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/script.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "crypto/sha1" 19 | "encoding/hex" 20 | "io" 21 | "strings" 22 | ) 23 | 24 | // Script encapsulates the source, hash and key count for a Lua script. See 25 | // http://redis.io/commands/eval for information on scripts in Redis. 26 | type Script struct { 27 | keyCount int 28 | src string 29 | hash string 30 | } 31 | 32 | // NewScript returns a new script object. If keyCount is greater than or equal 33 | // to zero, then the count is automatically inserted in the EVAL command 34 | // argument list. If keyCount is less than zero, then the application supplies 35 | // the count as the first value in the keysAndArgs argument to the Do, Send and 36 | // SendHash methods. 37 | func NewScript(keyCount int, src string) *Script { 38 | h := sha1.New() 39 | io.WriteString(h, src) 40 | return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))} 41 | } 42 | 43 | func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} { 44 | var args []interface{} 45 | if s.keyCount < 0 { 46 | args = make([]interface{}, 1+len(keysAndArgs)) 47 | args[0] = spec 48 | copy(args[1:], keysAndArgs) 49 | } else { 50 | args = make([]interface{}, 2+len(keysAndArgs)) 51 | args[0] = spec 52 | args[1] = s.keyCount 53 | copy(args[2:], keysAndArgs) 54 | } 55 | return args 56 | } 57 | 58 | // Do evaluates the script. Under the covers, Do optimistically evaluates the 59 | // script using the EVALSHA command. If the command fails because the script is 60 | // not loaded, then Do evaluates the script using the EVAL command (thus 61 | // causing the script to load). 62 | func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) { 63 | v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...) 64 | if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") { 65 | v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...) 66 | } 67 | return v, err 68 | } 69 | 70 | // SendHash evaluates the script without waiting for the reply. The script is 71 | // evaluated with the EVALSHA command. The application must ensure that the 72 | // script is loaded by a previous call to Send, Do or Load methods. 73 | func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error { 74 | return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...) 75 | } 76 | 77 | // Send evaluates the script without waiting for the reply. 78 | func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error { 79 | return c.Send("EVAL", s.args(s.src, keysAndArgs)...) 80 | } 81 | 82 | // Load loads the script without evaluating it. 83 | func (s *Script) Load(c Conn) error { 84 | _, err := c.Do("SCRIPT", "LOAD", s.src) 85 | return err 86 | } 87 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "log" 21 | ) 22 | 23 | // NewLoggingConn returns a logging wrapper around a connection. 24 | func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn { 25 | if prefix != "" { 26 | prefix = prefix + "." 27 | } 28 | return &loggingConn{conn, logger, prefix} 29 | } 30 | 31 | type loggingConn struct { 32 | Conn 33 | logger *log.Logger 34 | prefix string 35 | } 36 | 37 | func (c *loggingConn) Close() error { 38 | err := c.Conn.Close() 39 | var buf bytes.Buffer 40 | fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err) 41 | c.logger.Output(2, buf.String()) 42 | return err 43 | } 44 | 45 | func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) { 46 | const chop = 32 47 | switch v := v.(type) { 48 | case []byte: 49 | if len(v) > chop { 50 | fmt.Fprintf(buf, "%q...", v[:chop]) 51 | } else { 52 | fmt.Fprintf(buf, "%q", v) 53 | } 54 | case string: 55 | if len(v) > chop { 56 | fmt.Fprintf(buf, "%q...", v[:chop]) 57 | } else { 58 | fmt.Fprintf(buf, "%q", v) 59 | } 60 | case []interface{}: 61 | if len(v) == 0 { 62 | buf.WriteString("[]") 63 | } else { 64 | sep := "[" 65 | fin := "]" 66 | if len(v) > chop { 67 | v = v[:chop] 68 | fin = "...]" 69 | } 70 | for _, vv := range v { 71 | buf.WriteString(sep) 72 | c.printValue(buf, vv) 73 | sep = ", " 74 | } 75 | buf.WriteString(fin) 76 | } 77 | default: 78 | fmt.Fprint(buf, v) 79 | } 80 | } 81 | 82 | func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) { 83 | var buf bytes.Buffer 84 | fmt.Fprintf(&buf, "%s%s(", c.prefix, method) 85 | if method != "Receive" { 86 | buf.WriteString(commandName) 87 | for _, arg := range args { 88 | buf.WriteString(", ") 89 | c.printValue(&buf, arg) 90 | } 91 | } 92 | buf.WriteString(") -> (") 93 | if method != "Send" { 94 | c.printValue(&buf, reply) 95 | buf.WriteString(", ") 96 | } 97 | fmt.Fprintf(&buf, "%v)", err) 98 | c.logger.Output(3, buf.String()) 99 | } 100 | 101 | func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) { 102 | reply, err := c.Conn.Do(commandName, args...) 103 | c.print("Do", commandName, args, reply, err) 104 | return reply, err 105 | } 106 | 107 | func (c *loggingConn) Send(commandName string, args ...interface{}) error { 108 | err := c.Conn.Send(commandName, args...) 109 | c.print("Send", commandName, args, nil, err) 110 | return err 111 | } 112 | 113 | func (c *loggingConn) Receive() (interface{}, error) { 114 | reply, err := c.Conn.Receive() 115 | c.print("Receive", "", nil, reply, err) 116 | return reply, err 117 | } 118 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/info.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "net" 5 | "os" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/bsm/redeo/info" 10 | ) 11 | 12 | type ServerInfo struct { 13 | registry *info.Registry 14 | 15 | startTime time.Time 16 | port string 17 | socket string 18 | pid int 19 | 20 | clients *clients 21 | connections *info.Counter 22 | commands *info.Counter 23 | } 24 | 25 | // newServerInfo creates a new server info container 26 | func newServerInfo(config *Config, clients *clients) *ServerInfo { 27 | info := &ServerInfo{ 28 | registry: info.New(), 29 | startTime: time.Now(), 30 | connections: info.NewCounter(), 31 | commands: info.NewCounter(), 32 | clients: clients, 33 | } 34 | return info.withDefaults(config) 35 | } 36 | 37 | // ------------------------------------------------------------------------ 38 | 39 | // Section finds-or-creates an info section 40 | func (i *ServerInfo) Section(name string) *info.Section { return i.registry.Section(name) } 41 | 42 | // String generates an info string 43 | func (i *ServerInfo) String() string { return i.registry.String() } 44 | 45 | // ClientsLen returns the number of connected clients 46 | func (i *ServerInfo) ClientsLen() int { return i.clients.Len() } 47 | 48 | // Clients generates a slice of connected clients 49 | func (i *ServerInfo) Clients() []*Client { return i.clients.All() } 50 | 51 | // ClientsString generates a client list 52 | func (i *ServerInfo) ClientsString() string { 53 | str := "" 54 | for _, client := range i.Clients() { 55 | str += client.String() + "\n" 56 | } 57 | return str 58 | } 59 | 60 | // TotalConnections returns the total number of connections made since the 61 | // start of the server. 62 | func (i *ServerInfo) TotalConnections() int64 { return i.connections.Value() } 63 | 64 | // TotalCommands returns the total number of commands executed since the start 65 | // of the server. 66 | func (i *ServerInfo) TotalCommands() int64 { return i.commands.Value() } 67 | 68 | // ------------------------------------------------------------------------ 69 | 70 | // Apply default info 71 | func (i *ServerInfo) withDefaults(config *Config) *ServerInfo { 72 | _, port, _ := net.SplitHostPort(config.Addr) 73 | 74 | server := i.Section("Server") 75 | server.Register("process_id", info.PlainInt(os.Getpid())) 76 | server.Register("tcp_port", info.PlainString(port)) 77 | server.Register("unix_socket", info.PlainString(config.Socket)) 78 | server.Register("uptime_in_seconds", info.Callback(func() string { 79 | d := time.Now().Sub(i.startTime) / time.Second 80 | return strconv.FormatInt(int64(d), 10) 81 | })) 82 | server.Register("uptime_in_days", info.Callback(func() string { 83 | d := time.Now().Sub(i.startTime) / time.Hour / 24 84 | return strconv.FormatInt(int64(d), 10) 85 | })) 86 | 87 | clients := i.Section("Clients") 88 | clients.Register("connected_clients", info.Callback(func() string { 89 | return strconv.Itoa(i.ClientsLen()) 90 | })) 91 | 92 | stats := i.Section("Stats") 93 | stats.Register("total_connections_received", i.connections) 94 | stats.Register("total_commands_processed", i.commands) 95 | 96 | return i 97 | } 98 | 99 | // Callback to register a new client connection 100 | func (i *ServerInfo) onConnect() { i.connections.Inc(1) } 101 | 102 | // Callback to track processed command 103 | func (i *ServerInfo) onCommand() { i.commands.Inc(1) } 104 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/request_test.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "strings" 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | var _ = Describe("Request", func() { 14 | var successCases = []struct { 15 | r Request 16 | m string 17 | d string 18 | }{ 19 | {Request{Name: "ping"}, "PiNg\r\n", "inline ping"}, 20 | {Request{Name: "ping", Args: []string{}}, "*1\r\n$4\r\nPiNg\r\n", "bulk ping"}, 21 | {Request{Name: "get", Args: []string{"Xy"}}, "*2\r\n$3\r\nGET\r\n$2\r\nXy\r\n", "get"}, 22 | {Request{Name: "set", Args: []string{"k\r\ney", "va\r\nl"}}, "*3\r\n$3\r\nSET\r\n$5\r\nk\r\ney\r\n$5\r\nva\r\nl\r\n", "set"}, 23 | } 24 | 25 | var failureCases = []struct { 26 | e error 27 | m string 28 | d string 29 | }{ 30 | {io.EOF, "", "blank"}, 31 | {io.EOF, "\r\n", "blank with CRLF"}, 32 | {ErrInvalidRequest, "*x\r\n", "no bulk length"}, 33 | {ErrInvalidRequest, "*1\r\nping\r\n", "no argument length"}, 34 | {io.EOF, "*2\r\n$3\r\nget\r\n", "truncated message"}, 35 | {ErrInvalidRequest, "*2\r\n$x\r\nget\r\n", "missing argument len"}, 36 | {io.EOF, "*2\r\n$3\r\nge", "truncated argument"}, 37 | {ErrInvalidRequest, "*2\n$3\nget\n$1\nx\n", "wrong line breaks"}, 38 | } 39 | 40 | It("should parse successfully, consuming the full message", func() { 41 | for _, c := range successCases { 42 | rd := bufio.NewReader(strings.NewReader(c.m)) 43 | req, err := ParseRequest(rd) 44 | Expect(err).To(BeNil(), c.d) 45 | Expect(req).To(BeEquivalentTo(&c.r), c.d) 46 | 47 | more, err := rd.Peek(1) 48 | Expect(more).To(HaveLen(0)) 49 | Expect(err).To(Equal(io.EOF)) 50 | } 51 | }) 52 | 53 | It("should have client information", func() { 54 | cln := &Client{} 55 | req := &Request{client: cln} 56 | Expect(req.Client()).To(Equal(cln)) 57 | }) 58 | 59 | It("should parse chunks", func() { 60 | val := strings.Repeat("x", 1024) 61 | bio := bufio.NewReader(mockFD{s: "*3\r\n$3\r\nset\r\n$1\r\nx\r\n$1024\r\n" + val + "\r\n"}) 62 | req, err := ParseRequest(bio) 63 | Expect(err).NotTo(HaveOccurred()) 64 | Expect(req).NotTo(BeNil()) 65 | Expect(req.Args).To(HaveLen(2)) 66 | Expect(req.Args[1]).To(HaveLen(1024)) 67 | Expect(req.Args[1][1020:]).To(Equal("xxxx")) 68 | }) 69 | 70 | It("should support pipelining", func() { 71 | msg := "" 72 | for _, c := range successCases { 73 | msg = msg + c.m 74 | } 75 | rdr := bufio.NewReader(strings.NewReader(msg)) 76 | 77 | for _, c := range successCases { 78 | req, err := ParseRequest(rdr) 79 | Expect(err).To(BeNil(), c.d) 80 | Expect(req).To(BeEquivalentTo(&c.r), c.d) 81 | } 82 | }) 83 | 84 | It("should fail on invalid inputs", func() { 85 | for _, c := range failureCases { 86 | req, err := ParseRequest(bufio.NewReader(strings.NewReader(c.m))) 87 | Expect(req).To(BeNil(), c.d) 88 | Expect(err).To(Equal(c.e), c.d) 89 | } 90 | }) 91 | 92 | }) 93 | 94 | func BenchmarkParseRequest_Inline(b *testing.B) { 95 | for i := 0; i < b.N; i++ { 96 | ParseRequest(bufio.NewReader(strings.NewReader("ping\r\n"))) 97 | } 98 | } 99 | 100 | func BenchmarkParseRequest_Bulk(b *testing.B) { 101 | for i := 0; i < b.N; i++ { 102 | ParseRequest(bufio.NewReader(strings.NewReader("*2\r\n$3\r\nget\r\n$1\r\nx\r\n"))) 103 | } 104 | } 105 | 106 | // MOCKS 107 | 108 | type mockFD struct { 109 | s string 110 | pos int 111 | } 112 | 113 | func (r mockFD) Read(buf []byte) (int, error) { 114 | n := len(r.s) - r.pos 115 | if n > 100 { 116 | n = 100 117 | } 118 | 119 | copy(buf, r.s[r.pos:r.pos+n]) 120 | r.pos += n 121 | return n, nil 122 | } 123 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/cmd_transactions.go: -------------------------------------------------------------------------------- 1 | // Commands from http://redis.io/commands#transactions 2 | 3 | package miniredis 4 | 5 | import ( 6 | "github.com/bsm/redeo" 7 | ) 8 | 9 | // commandsTransaction handles MULTI &c. 10 | func commandsTransaction(m *Miniredis, srv *redeo.Server) { 11 | srv.HandleFunc("DISCARD", m.cmdDiscard) 12 | srv.HandleFunc("EXEC", m.cmdExec) 13 | srv.HandleFunc("MULTI", m.cmdMulti) 14 | srv.HandleFunc("UNWATCH", m.cmdUnwatch) 15 | srv.HandleFunc("WATCH", m.cmdWatch) 16 | } 17 | 18 | // MULTI 19 | func (m *Miniredis) cmdMulti(out *redeo.Responder, r *redeo.Request) error { 20 | if len(r.Args) != 0 { 21 | return r.WrongNumberOfArgs() 22 | } 23 | if !m.handleAuth(r.Client(), out) { 24 | return nil 25 | } 26 | 27 | ctx := getCtx(r.Client()) 28 | 29 | if inTx(ctx) { 30 | return redeo.ClientError("MULTI calls can not be nested") 31 | } 32 | 33 | startTx(ctx) 34 | 35 | out.WriteOK() 36 | return nil 37 | } 38 | 39 | // EXEC 40 | func (m *Miniredis) cmdExec(out *redeo.Responder, r *redeo.Request) error { 41 | if len(r.Args) != 0 { 42 | setDirty(r.Client()) 43 | return r.WrongNumberOfArgs() 44 | } 45 | if !m.handleAuth(r.Client(), out) { 46 | return nil 47 | } 48 | 49 | ctx := getCtx(r.Client()) 50 | 51 | if !inTx(ctx) { 52 | return redeo.ClientError("EXEC without MULTI") 53 | } 54 | 55 | if dirtyTx(ctx) { 56 | out.WriteErrorString("EXECABORT Transaction discarded because of previous errors.") 57 | return nil 58 | } 59 | 60 | m.Lock() 61 | defer m.Unlock() 62 | 63 | // Check WATCHed keys. 64 | for t, version := range ctx.watch { 65 | if m.db(t.db).keyVersion[t.key] > version { 66 | // Abort! Abort! 67 | stopTx(ctx) 68 | out.WriteBulkLen(0) 69 | return nil 70 | } 71 | } 72 | 73 | out.WriteBulkLen(len(ctx.transaction)) 74 | for _, cb := range ctx.transaction { 75 | cb(out, ctx) 76 | } 77 | // wake up anyone who waits on anything. 78 | m.signal.Broadcast() 79 | 80 | stopTx(ctx) 81 | return nil 82 | } 83 | 84 | // DISCARD 85 | func (m *Miniredis) cmdDiscard(out *redeo.Responder, r *redeo.Request) error { 86 | if len(r.Args) != 0 { 87 | setDirty(r.Client()) 88 | return r.WrongNumberOfArgs() 89 | } 90 | if !m.handleAuth(r.Client(), out) { 91 | return nil 92 | } 93 | 94 | ctx := getCtx(r.Client()) 95 | if !inTx(ctx) { 96 | return redeo.ClientError("DISCARD without MULTI") 97 | } 98 | 99 | stopTx(ctx) 100 | out.WriteOK() 101 | return nil 102 | } 103 | 104 | // WATCH 105 | func (m *Miniredis) cmdWatch(out *redeo.Responder, r *redeo.Request) error { 106 | if len(r.Args) == 0 { 107 | setDirty(r.Client()) 108 | return r.WrongNumberOfArgs() 109 | } 110 | if !m.handleAuth(r.Client(), out) { 111 | return nil 112 | } 113 | 114 | ctx := getCtx(r.Client()) 115 | if inTx(ctx) { 116 | return redeo.ClientError("WATCH in MULTI") 117 | } 118 | 119 | m.Lock() 120 | defer m.Unlock() 121 | db := m.db(ctx.selectedDB) 122 | 123 | for _, key := range r.Args { 124 | watch(db, ctx, key) 125 | } 126 | out.WriteOK() 127 | return nil 128 | } 129 | 130 | // UNWATCH 131 | func (m *Miniredis) cmdUnwatch(out *redeo.Responder, r *redeo.Request) error { 132 | if len(r.Args) != 0 { 133 | setDirty(r.Client()) 134 | return r.WrongNumberOfArgs() 135 | } 136 | if !m.handleAuth(r.Client(), out) { 137 | return nil 138 | } 139 | 140 | // Doesn't matter if UNWATCH is in a TX or not. Looks like a Redis bug to me. 141 | unwatch(getCtx(r.Client())) 142 | 143 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 144 | // Do nothing if it's called in a transaction. 145 | out.WriteOK() 146 | }) 147 | } 148 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redisx/connmux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redisx 16 | 17 | import ( 18 | "errors" 19 | "sync" 20 | 21 | "github.com/garyburd/redigo/internal" 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | // ConnMux multiplexes one or more connections to a single underlying 26 | // connection. The ConnMux connections do not support concurrency, commands 27 | // that associate server side state with the connection or commands that put 28 | // the connection in a special mode. 29 | type ConnMux struct { 30 | c redis.Conn 31 | 32 | sendMu sync.Mutex 33 | sendID uint 34 | 35 | recvMu sync.Mutex 36 | recvID uint 37 | recvWait map[uint]chan struct{} 38 | } 39 | 40 | func NewConnMux(c redis.Conn) *ConnMux { 41 | return &ConnMux{c: c, recvWait: make(map[uint]chan struct{})} 42 | } 43 | 44 | // Get gets a connection. The application must close the returned connection. 45 | func (p *ConnMux) Get() redis.Conn { 46 | c := &muxConn{p: p} 47 | c.ids = c.buf[:0] 48 | return c 49 | } 50 | 51 | // Close closes the underlying connection. 52 | func (p *ConnMux) Close() error { 53 | return p.c.Close() 54 | } 55 | 56 | type muxConn struct { 57 | p *ConnMux 58 | ids []uint 59 | buf [8]uint 60 | } 61 | 62 | func (c *muxConn) send(flush bool, cmd string, args ...interface{}) error { 63 | if internal.LookupCommandInfo(cmd).Set != 0 { 64 | return errors.New("command not supported by mux pool") 65 | } 66 | p := c.p 67 | p.sendMu.Lock() 68 | id := p.sendID 69 | c.ids = append(c.ids, id) 70 | p.sendID++ 71 | err := p.c.Send(cmd, args...) 72 | if flush { 73 | err = p.c.Flush() 74 | } 75 | p.sendMu.Unlock() 76 | return err 77 | } 78 | 79 | func (c *muxConn) Send(cmd string, args ...interface{}) error { 80 | return c.send(false, cmd, args...) 81 | } 82 | 83 | func (c *muxConn) Flush() error { 84 | p := c.p 85 | p.sendMu.Lock() 86 | err := p.c.Flush() 87 | p.sendMu.Unlock() 88 | return err 89 | } 90 | 91 | func (c *muxConn) Receive() (interface{}, error) { 92 | if len(c.ids) == 0 { 93 | return nil, errors.New("mux pool underflow") 94 | } 95 | 96 | id := c.ids[0] 97 | c.ids = c.ids[1:] 98 | if len(c.ids) == 0 { 99 | c.ids = c.buf[:0] 100 | } 101 | 102 | p := c.p 103 | p.recvMu.Lock() 104 | if p.recvID != id { 105 | ch := make(chan struct{}) 106 | p.recvWait[id] = ch 107 | p.recvMu.Unlock() 108 | <-ch 109 | p.recvMu.Lock() 110 | if p.recvID != id { 111 | panic("out of sync") 112 | } 113 | } 114 | 115 | v, err := p.c.Receive() 116 | 117 | id++ 118 | p.recvID = id 119 | ch, ok := p.recvWait[id] 120 | if ok { 121 | delete(p.recvWait, id) 122 | } 123 | p.recvMu.Unlock() 124 | if ok { 125 | ch <- struct{}{} 126 | } 127 | 128 | return v, err 129 | } 130 | 131 | func (c *muxConn) Close() error { 132 | var err error 133 | if len(c.ids) == 0 { 134 | return nil 135 | } 136 | c.Flush() 137 | for _ = range c.ids { 138 | _, err = c.Receive() 139 | } 140 | return err 141 | } 142 | 143 | func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, error) { 144 | if err := c.send(true, cmd, args...); err != nil { 145 | return nil, err 146 | } 147 | return c.Receive() 148 | } 149 | 150 | func (c *muxConn) Err() error { 151 | return c.p.c.Err() 152 | } 153 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/server_test.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net" 7 | "strings" 8 | 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | var _ = Describe("Server", func() { 14 | var subject *Server 15 | 16 | var pong = func(out *Responder, _ *Request) error { 17 | out.WriteInlineString("PONG") 18 | return nil 19 | } 20 | 21 | var blank = func(out *Responder, _ *Request) error { 22 | return nil 23 | } 24 | 25 | var failing = func(out *Responder, _ *Request) error { 26 | return io.EOF 27 | } 28 | 29 | var echo = func(out *Responder, req *Request) error { 30 | if len(req.Args) != 1 { 31 | return WrongNumberOfArgs(req.Name) 32 | } 33 | out.WriteString(req.Args[0]) 34 | return nil 35 | } 36 | 37 | BeforeEach(func() { 38 | subject = NewServer(nil) 39 | }) 40 | 41 | It("should fallback on default config", func() { 42 | Expect(subject.config).To(Equal(DefaultConfig)) 43 | }) 44 | 45 | It("should listen/serve/close", func() { 46 | subject.HandleFunc("pInG", pong) 47 | 48 | // Listen to connections 49 | ec := make(chan error, 1) 50 | go func() { 51 | ec <- subject.ListenAndServe() 52 | }() 53 | 54 | // Connect client 55 | var clnt net.Conn 56 | Eventually(func() (err error) { 57 | clnt, err = net.Dial("tcp", "127.0.0.1:9736") 58 | return err 59 | }).ShouldNot(HaveOccurred()) 60 | defer clnt.Close() 61 | 62 | // Ping 63 | pong := make([]byte, 10) 64 | _, err := clnt.Write([]byte("PING\r\n")) 65 | Expect(err).NotTo(HaveOccurred()) 66 | n, err := clnt.Read(pong) 67 | Expect(err).NotTo(HaveOccurred()) 68 | Expect(string(pong[:n])).To(Equal("+PONG\r\n")) 69 | 70 | // Close 71 | err = subject.Close() 72 | Expect(err).NotTo(HaveOccurred()) 73 | 74 | // Expect to exit 75 | err = <-ec 76 | Expect(err).To(HaveOccurred()) 77 | Expect(err.Error()).To(ContainSubstring("closed")) 78 | 79 | // Ping again 80 | _, err = clnt.Write([]byte("PING\r\n")) 81 | Expect(err).NotTo(HaveOccurred()) 82 | _, err = clnt.Read(pong) 83 | Expect(err).To(Equal(io.EOF)) 84 | }) 85 | 86 | It("should register handlers", func() { 87 | subject.HandleFunc("pInG", pong) 88 | Expect(subject.commands).To(HaveLen(1)) 89 | Expect(subject.commands).To(HaveKey("ping")) 90 | }) 91 | 92 | Describe("request handling", func() { 93 | 94 | It("should apply requests", func() { 95 | subject.HandleFunc("echo", echo) 96 | 97 | client := NewClient(&mockConn{}) 98 | 99 | w := &bytes.Buffer{} 100 | ok := subject.apply(&Request{Name: "echo", client: client}, w) 101 | Expect(ok).To(BeTrue()) 102 | Expect(w.String()).To(Equal("-ERR wrong number of arguments for 'echo' command\r\n")) 103 | 104 | w = &bytes.Buffer{} 105 | ok = subject.apply(&Request{Name: "echo", Args: []string{"SAY HI!"}}, w) 106 | Expect(ok).To(BeTrue()) 107 | Expect(w.String()).To(Equal("$7\r\nSAY HI!\r\n")) 108 | 109 | w = &bytes.Buffer{} 110 | ok = subject.apply(&Request{Name: "echo", Args: []string{strings.Repeat("x", 100000)}}, w) 111 | Expect(ok).To(BeTrue()) 112 | Expect(w.Len()).To(Equal(100011)) 113 | Expect(w.String()[:9]).To(Equal("$100000\r\n")) 114 | 115 | Expect(client.lastCommand).To(Equal("echo")) 116 | Expect(subject.Info().TotalCommands()).To(Equal(int64(3))) 117 | }) 118 | 119 | It("should write errors if they occur", func() { 120 | subject.HandleFunc("failing", failing) 121 | 122 | w := &bytes.Buffer{} 123 | ok := subject.apply(&Request{Name: "failing"}, w) 124 | Expect(ok).To(BeTrue()) 125 | Expect(w.String()).To(Equal("-ERR EOF\r\n")) 126 | }) 127 | 128 | It("should auto-respond with OK when nothing written", func() { 129 | subject.HandleFunc("blank", blank) 130 | 131 | w := &bytes.Buffer{} 132 | ok := subject.apply(&Request{Name: "blank"}, w) 133 | Expect(ok).To(BeTrue()) 134 | Expect(w.String()).To(Equal("+OK\r\n")) 135 | }) 136 | 137 | It("should return false on write failures", func() { 138 | subject.HandleFunc("blank", blank) 139 | 140 | w := &badWriter{} 141 | ok := subject.apply(&Request{Name: "blank"}, w) 142 | Expect(ok).To(BeFalse()) 143 | Expect(w.String()).To(Equal("+OK\r\n")) 144 | }) 145 | 146 | }) 147 | }) 148 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import "errors" 18 | 19 | // Subscription represents a subscribe or unsubscribe notification. 20 | type Subscription struct { 21 | 22 | // Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe" 23 | Kind string 24 | 25 | // The channel that was changed. 26 | Channel string 27 | 28 | // The current number of subscriptions for connection. 29 | Count int 30 | } 31 | 32 | // Message represents a message notification. 33 | type Message struct { 34 | 35 | // The originating channel. 36 | Channel string 37 | 38 | // The message data. 39 | Data []byte 40 | } 41 | 42 | // PMessage represents a pmessage notification. 43 | type PMessage struct { 44 | 45 | // The matched pattern. 46 | Pattern string 47 | 48 | // The originating channel. 49 | Channel string 50 | 51 | // The message data. 52 | Data []byte 53 | } 54 | 55 | // Pong represents a pubsub pong notification. 56 | type Pong struct { 57 | Data string 58 | } 59 | 60 | // PubSubConn wraps a Conn with convenience methods for subscribers. 61 | type PubSubConn struct { 62 | Conn Conn 63 | } 64 | 65 | // Close closes the connection. 66 | func (c PubSubConn) Close() error { 67 | return c.Conn.Close() 68 | } 69 | 70 | // Subscribe subscribes the connection to the specified channels. 71 | func (c PubSubConn) Subscribe(channel ...interface{}) error { 72 | c.Conn.Send("SUBSCRIBE", channel...) 73 | return c.Conn.Flush() 74 | } 75 | 76 | // PSubscribe subscribes the connection to the given patterns. 77 | func (c PubSubConn) PSubscribe(channel ...interface{}) error { 78 | c.Conn.Send("PSUBSCRIBE", channel...) 79 | return c.Conn.Flush() 80 | } 81 | 82 | // Unsubscribe unsubscribes the connection from the given channels, or from all 83 | // of them if none is given. 84 | func (c PubSubConn) Unsubscribe(channel ...interface{}) error { 85 | c.Conn.Send("UNSUBSCRIBE", channel...) 86 | return c.Conn.Flush() 87 | } 88 | 89 | // PUnsubscribe unsubscribes the connection from the given patterns, or from all 90 | // of them if none is given. 91 | func (c PubSubConn) PUnsubscribe(channel ...interface{}) error { 92 | c.Conn.Send("PUNSUBSCRIBE", channel...) 93 | return c.Conn.Flush() 94 | } 95 | 96 | // Ping sends a PING to the server with the specified data. 97 | func (c PubSubConn) Ping(data string) error { 98 | c.Conn.Send("PING", data) 99 | return c.Conn.Flush() 100 | } 101 | 102 | // Receive returns a pushed message as a Subscription, Message, PMessage, Pong 103 | // or error. The return value is intended to be used directly in a type switch 104 | // as illustrated in the PubSubConn example. 105 | func (c PubSubConn) Receive() interface{} { 106 | reply, err := Values(c.Conn.Receive()) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | var kind string 112 | reply, err = Scan(reply, &kind) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | switch kind { 118 | case "message": 119 | var m Message 120 | if _, err := Scan(reply, &m.Channel, &m.Data); err != nil { 121 | return err 122 | } 123 | return m 124 | case "pmessage": 125 | var pm PMessage 126 | if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil { 127 | return err 128 | } 129 | return pm 130 | case "subscribe", "psubscribe", "unsubscribe", "punsubscribe": 131 | s := Subscription{Kind: kind} 132 | if _, err := Scan(reply, &s.Channel, &s.Count); err != nil { 133 | return err 134 | } 135 | return s 136 | case "pong": 137 | var p Pong 138 | if _, err := Scan(reply, &p.Data); err != nil { 139 | return err 140 | } 141 | return p 142 | } 143 | return errors.New("redigo: unknown pubsub notification") 144 | } 145 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/reply_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | type valueError struct { 26 | v interface{} 27 | err error 28 | } 29 | 30 | func ve(v interface{}, err error) valueError { 31 | return valueError{v, err} 32 | } 33 | 34 | var replyTests = []struct { 35 | name interface{} 36 | actual valueError 37 | expected valueError 38 | }{ 39 | { 40 | "ints([v1, v2])", 41 | ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)), 42 | ve([]int{4, 5}, nil), 43 | }, 44 | { 45 | "ints(nil)", 46 | ve(redis.Ints(nil, nil)), 47 | ve([]int(nil), redis.ErrNil), 48 | }, 49 | { 50 | "strings([v1, v2])", 51 | ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 52 | ve([]string{"v1", "v2"}, nil), 53 | }, 54 | { 55 | "strings(nil)", 56 | ve(redis.Strings(nil, nil)), 57 | ve([]string(nil), redis.ErrNil), 58 | }, 59 | { 60 | "byteslices([v1, v2])", 61 | ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 62 | ve([][]byte{[]byte("v1"), []byte("v2")}, nil), 63 | }, 64 | { 65 | "byteslices(nil)", 66 | ve(redis.ByteSlices(nil, nil)), 67 | ve([][]byte(nil), redis.ErrNil), 68 | }, 69 | { 70 | "values([v1, v2])", 71 | ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 72 | ve([]interface{}{[]byte("v1"), []byte("v2")}, nil), 73 | }, 74 | { 75 | "values(nil)", 76 | ve(redis.Values(nil, nil)), 77 | ve([]interface{}(nil), redis.ErrNil), 78 | }, 79 | { 80 | "float64(1.0)", 81 | ve(redis.Float64([]byte("1.0"), nil)), 82 | ve(float64(1.0), nil), 83 | }, 84 | { 85 | "float64(nil)", 86 | ve(redis.Float64(nil, nil)), 87 | ve(float64(0.0), redis.ErrNil), 88 | }, 89 | { 90 | "uint64(1)", 91 | ve(redis.Uint64(int64(1), nil)), 92 | ve(uint64(1), nil), 93 | }, 94 | { 95 | "uint64(-1)", 96 | ve(redis.Uint64(int64(-1), nil)), 97 | ve(uint64(0), redis.ErrNegativeInt), 98 | }, 99 | } 100 | 101 | func TestReply(t *testing.T) { 102 | for _, rt := range replyTests { 103 | if rt.actual.err != rt.expected.err { 104 | t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err) 105 | continue 106 | } 107 | if !reflect.DeepEqual(rt.actual.v, rt.expected.v) { 108 | t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v) 109 | } 110 | } 111 | } 112 | 113 | // dial wraps DialDefaultServer() with a more suitable function name for examples. 114 | func dial() (redis.Conn, error) { 115 | return redis.DialDefaultServer() 116 | } 117 | 118 | func ExampleBool() { 119 | c, err := dial() 120 | if err != nil { 121 | fmt.Println(err) 122 | return 123 | } 124 | defer c.Close() 125 | 126 | c.Do("SET", "foo", 1) 127 | exists, _ := redis.Bool(c.Do("EXISTS", "foo")) 128 | fmt.Printf("%#v\n", exists) 129 | // Output: 130 | // true 131 | } 132 | 133 | func ExampleInt() { 134 | c, err := dial() 135 | if err != nil { 136 | fmt.Println(err) 137 | return 138 | } 139 | defer c.Close() 140 | 141 | c.Do("SET", "k1", 1) 142 | n, _ := redis.Int(c.Do("GET", "k1")) 143 | fmt.Printf("%#v\n", n) 144 | n, _ = redis.Int(c.Do("INCR", "k1")) 145 | fmt.Printf("%#v\n", n) 146 | // Output: 147 | // 1 148 | // 2 149 | } 150 | 151 | func ExampleInts() { 152 | c, err := dial() 153 | if err != nil { 154 | fmt.Println(err) 155 | return 156 | } 157 | defer c.Close() 158 | 159 | c.Do("SADD", "set_with_integers", 4, 5, 6) 160 | ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers")) 161 | fmt.Printf("%#v\n", ints) 162 | // Output: 163 | // []int{4, 5, 6} 164 | } 165 | 166 | func ExampleString() { 167 | c, err := dial() 168 | if err != nil { 169 | fmt.Println(err) 170 | return 171 | } 172 | defer c.Close() 173 | 174 | c.Do("SET", "hello", "world") 175 | s, err := redis.String(c.Do("GET", "hello")) 176 | fmt.Printf("%#v\n", s) 177 | // Output: 178 | // "world" 179 | } 180 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/miniredis_test.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/garyburd/redigo/redis" 7 | ) 8 | 9 | // Test starting/stopping a server 10 | func TestServer(t *testing.T) { 11 | s, err := Run() 12 | ok(t, err) 13 | defer s.Close() 14 | 15 | c, err := redis.Dial("tcp", s.Addr()) 16 | ok(t, err) 17 | _, err = c.Do("PING") 18 | ok(t, err) 19 | 20 | // A single client 21 | equals(t, 1, s.CurrentConnectionCount()) 22 | equals(t, 1, s.TotalConnectionCount()) 23 | equals(t, 1, s.CommandCount()) 24 | _, err = c.Do("PING") 25 | ok(t, err) 26 | equals(t, 2, s.CommandCount()) 27 | } 28 | 29 | func TestMultipleServers(t *testing.T) { 30 | s1, err := Run() 31 | ok(t, err) 32 | s2, err := Run() 33 | ok(t, err) 34 | if s1.Addr() == s2.Addr() { 35 | t.Fatal("Non-unique addresses", s1.Addr(), s2.Addr()) 36 | } 37 | 38 | s2.Close() 39 | s1.Close() 40 | // Closing multiple times is fine 41 | go s1.Close() 42 | go s1.Close() 43 | s1.Close() 44 | } 45 | 46 | func TestRestart(t *testing.T) { 47 | s, err := Run() 48 | ok(t, err) 49 | addr := s.Addr() 50 | 51 | s.Set("color", "red") 52 | 53 | s.Close() 54 | err = s.Restart() 55 | ok(t, err) 56 | if s.Addr() != addr { 57 | t.Fatal("should be the same address") 58 | } 59 | 60 | c, err := redis.Dial("tcp", s.Addr()) 61 | ok(t, err) 62 | _, err = c.Do("PING") 63 | ok(t, err) 64 | 65 | red, err := redis.String(c.Do("GET", "color")) 66 | ok(t, err) 67 | if have, want := red, "red"; have != want { 68 | t.Errorf("have: %s, want: %s", have, want) 69 | } 70 | } 71 | 72 | func TestDump(t *testing.T) { 73 | s, err := Run() 74 | ok(t, err) 75 | s.Set("aap", "noot") 76 | s.Set("vuur", "mies") 77 | s.HSet("ahash", "aap", "noot") 78 | s.HSet("ahash", "vuur", "mies") 79 | if have, want := s.Dump(), `- aap 80 | "noot" 81 | - ahash 82 | aap: "noot" 83 | vuur: "mies" 84 | - vuur 85 | "mies" 86 | `; have != want { 87 | t.Errorf("have: %q, want: %q", have, want) 88 | } 89 | 90 | // Tricky whitespace 91 | s.Select(1) 92 | s.Set("whitespace", "foo\nbar\tbaz!") 93 | if have, want := s.Dump(), `- whitespace 94 | "foo\nbar\tbaz!" 95 | `; have != want { 96 | t.Errorf("have: %q, want: %q", have, want) 97 | } 98 | 99 | // Long key 100 | s.Select(2) 101 | s.Set("long", "This is a rather long key, with some fox jumping over a fence or something.") 102 | s.Set("countonme", "0123456789012345678901234567890123456789012345678901234567890123456789") 103 | s.HSet("hlong", "long", "This is another rather long key, with some fox jumping over a fence or something.") 104 | if have, want := s.Dump(), `- countonme 105 | "01234567890123456789012345678901234567890123456789012"...(70) 106 | - hlong 107 | long: "This is another rather long key, with some fox jumpin"...(81) 108 | - long 109 | "This is a rather long key, with some fox jumping over"...(75) 110 | `; have != want { 111 | t.Errorf("have: %q, want: %q", have, want) 112 | } 113 | } 114 | 115 | func TestDumpList(t *testing.T) { 116 | s, err := Run() 117 | ok(t, err) 118 | s.Push("elements", "earth") 119 | s.Push("elements", "wind") 120 | s.Push("elements", "fire") 121 | if have, want := s.Dump(), `- elements 122 | "earth" 123 | "wind" 124 | "fire" 125 | `; have != want { 126 | t.Errorf("have: %q, want: %q", have, want) 127 | } 128 | } 129 | 130 | func TestDumpSet(t *testing.T) { 131 | s, err := Run() 132 | ok(t, err) 133 | s.SetAdd("elements", "earth") 134 | s.SetAdd("elements", "wind") 135 | s.SetAdd("elements", "fire") 136 | if have, want := s.Dump(), `- elements 137 | "earth" 138 | "fire" 139 | "wind" 140 | `; have != want { 141 | t.Errorf("have: %q, want: %q", have, want) 142 | } 143 | } 144 | 145 | func TestDumpSortedSet(t *testing.T) { 146 | s, err := Run() 147 | ok(t, err) 148 | s.ZAdd("elements", 2.0, "wind") 149 | s.ZAdd("elements", 3.0, "earth") 150 | s.ZAdd("elements", 1.0, "fire") 151 | if have, want := s.Dump(), `- elements 152 | 1.000000: "fire" 153 | 2.000000: "wind" 154 | 3.000000: "earth" 155 | `; have != want { 156 | t.Errorf("have: %q, want: %q", have, want) 157 | } 158 | } 159 | 160 | func TestKeysAndFlush(t *testing.T) { 161 | s, err := Run() 162 | ok(t, err) 163 | s.Set("aap", "noot") 164 | s.Set("vuur", "mies") 165 | s.Set("muur", "oom") 166 | s.HSet("hash", "key", "value") 167 | equals(t, []string{"aap", "hash", "muur", "vuur"}, s.Keys()) 168 | 169 | s.Select(1) 170 | s.Set("1aap", "1noot") 171 | equals(t, []string{"1aap"}, s.Keys()) 172 | 173 | s.Select(0) 174 | s.FlushDB() 175 | equals(t, []string{}, s.Keys()) 176 | s.Select(1) 177 | equals(t, []string{"1aap"}, s.Keys()) 178 | 179 | s.Select(0) 180 | s.FlushAll() 181 | equals(t, []string{}, s.Keys()) 182 | s.Select(1) 183 | equals(t, []string{}, s.Keys()) 184 | } 185 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "sync" 21 | "testing" 22 | 23 | "github.com/garyburd/redigo/redis" 24 | ) 25 | 26 | func publish(channel, value interface{}) { 27 | c, err := dial() 28 | if err != nil { 29 | fmt.Println(err) 30 | return 31 | } 32 | defer c.Close() 33 | c.Do("PUBLISH", channel, value) 34 | } 35 | 36 | // Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine. 37 | func ExamplePubSubConn() { 38 | c, err := dial() 39 | if err != nil { 40 | fmt.Println(err) 41 | return 42 | } 43 | defer c.Close() 44 | var wg sync.WaitGroup 45 | wg.Add(2) 46 | 47 | psc := redis.PubSubConn{Conn: c} 48 | 49 | // This goroutine receives and prints pushed notifications from the server. 50 | // The goroutine exits when the connection is unsubscribed from all 51 | // channels or there is an error. 52 | go func() { 53 | defer wg.Done() 54 | for { 55 | switch n := psc.Receive().(type) { 56 | case redis.Message: 57 | fmt.Printf("Message: %s %s\n", n.Channel, n.Data) 58 | case redis.PMessage: 59 | fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data) 60 | case redis.Subscription: 61 | fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count) 62 | if n.Count == 0 { 63 | return 64 | } 65 | case error: 66 | fmt.Printf("error: %v\n", n) 67 | return 68 | } 69 | } 70 | }() 71 | 72 | // This goroutine manages subscriptions for the connection. 73 | go func() { 74 | defer wg.Done() 75 | 76 | psc.Subscribe("example") 77 | psc.PSubscribe("p*") 78 | 79 | // The following function calls publish a message using another 80 | // connection to the Redis server. 81 | publish("example", "hello") 82 | publish("example", "world") 83 | publish("pexample", "foo") 84 | publish("pexample", "bar") 85 | 86 | // Unsubscribe from all connections. This will cause the receiving 87 | // goroutine to exit. 88 | psc.Unsubscribe() 89 | psc.PUnsubscribe() 90 | }() 91 | 92 | wg.Wait() 93 | 94 | // Output: 95 | // Subscription: subscribe example 1 96 | // Subscription: psubscribe p* 2 97 | // Message: example hello 98 | // Message: example world 99 | // PMessage: p* pexample foo 100 | // PMessage: p* pexample bar 101 | // Subscription: unsubscribe example 1 102 | // Subscription: punsubscribe p* 0 103 | } 104 | 105 | func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) { 106 | actual := c.Receive() 107 | if !reflect.DeepEqual(actual, expected) { 108 | t.Errorf("%s = %v, want %v", message, actual, expected) 109 | } 110 | } 111 | 112 | func TestPushed(t *testing.T) { 113 | pc, err := redis.DialDefaultServer() 114 | if err != nil { 115 | t.Fatalf("error connection to database, %v", err) 116 | } 117 | defer pc.Close() 118 | 119 | sc, err := redis.DialDefaultServer() 120 | if err != nil { 121 | t.Fatalf("error connection to database, %v", err) 122 | } 123 | defer sc.Close() 124 | 125 | c := redis.PubSubConn{Conn: sc} 126 | 127 | c.Subscribe("c1") 128 | expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1}) 129 | c.Subscribe("c2") 130 | expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2}) 131 | c.PSubscribe("p1") 132 | expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3}) 133 | c.PSubscribe("p2") 134 | expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4}) 135 | c.PUnsubscribe() 136 | expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3}) 137 | expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2}) 138 | 139 | pc.Do("PUBLISH", "c1", "hello") 140 | expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")}) 141 | 142 | c.Ping("hello") 143 | expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"}) 144 | 145 | c.Conn.Send("PING") 146 | c.Conn.Flush() 147 | expectPushed(t, c, `Send("PING")`, redis.Pong{}) 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/test_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "bufio" 19 | "errors" 20 | "flag" 21 | "fmt" 22 | "io" 23 | "io/ioutil" 24 | "os" 25 | "os/exec" 26 | "strconv" 27 | "strings" 28 | "sync" 29 | "testing" 30 | "time" 31 | ) 32 | 33 | func SetNowFunc(f func() time.Time) { 34 | nowFunc = f 35 | } 36 | 37 | var ( 38 | ErrNegativeInt = errNegativeInt 39 | 40 | serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary") 41 | serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers") 42 | serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`") 43 | serverLog = ioutil.Discard 44 | 45 | defaultServerMu sync.Mutex 46 | defaultServer *Server 47 | defaultServerErr error 48 | ) 49 | 50 | type Server struct { 51 | name string 52 | cmd *exec.Cmd 53 | done chan struct{} 54 | } 55 | 56 | func NewServer(name string, args ...string) (*Server, error) { 57 | s := &Server{ 58 | name: name, 59 | cmd: exec.Command(*serverPath, args...), 60 | done: make(chan struct{}), 61 | } 62 | 63 | r, err := s.cmd.StdoutPipe() 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | err = s.cmd.Start() 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | ready := make(chan error, 1) 74 | go s.watch(r, ready) 75 | 76 | select { 77 | case err = <-ready: 78 | case <-time.After(time.Second * 10): 79 | err = errors.New("timeout waiting for server to start") 80 | } 81 | 82 | if err != nil { 83 | s.Stop() 84 | return nil, err 85 | } 86 | 87 | return s, nil 88 | } 89 | 90 | func (s *Server) watch(r io.Reader, ready chan error) { 91 | fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name) 92 | var listening bool 93 | var text string 94 | scn := bufio.NewScanner(r) 95 | for scn.Scan() { 96 | text = scn.Text() 97 | fmt.Fprintf(serverLog, "%s\n", text) 98 | if !listening { 99 | if strings.Contains(text, "The server is now ready to accept connections on port") { 100 | listening = true 101 | ready <- nil 102 | } 103 | } 104 | } 105 | if !listening { 106 | ready <- fmt.Errorf("server exited: %s", text) 107 | } 108 | s.cmd.Wait() 109 | fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name) 110 | close(s.done) 111 | } 112 | 113 | func (s *Server) Stop() { 114 | s.cmd.Process.Signal(os.Interrupt) 115 | <-s.done 116 | } 117 | 118 | // stopDefaultServer stops the server created by DialDefaultServer. 119 | func stopDefaultServer() { 120 | defaultServerMu.Lock() 121 | defer defaultServerMu.Unlock() 122 | if defaultServer != nil { 123 | defaultServer.Stop() 124 | defaultServer = nil 125 | } 126 | } 127 | 128 | // startDefaultServer starts the default server if not already running. 129 | func startDefaultServer() error { 130 | defaultServerMu.Lock() 131 | defer defaultServerMu.Unlock() 132 | if defaultServer != nil || defaultServerErr != nil { 133 | return defaultServerErr 134 | } 135 | defaultServer, defaultServerErr = NewServer( 136 | "default", 137 | "--port", strconv.Itoa(*serverBasePort), 138 | "--save", "", 139 | "--appendonly", "no") 140 | return defaultServerErr 141 | } 142 | 143 | // DialDefaultServer starts the test server if not already started and dials a 144 | // connection to the server. 145 | func DialDefaultServer() (Conn, error) { 146 | if err := startDefaultServer(); err != nil { 147 | return nil, err 148 | } 149 | c, err := Dial("tcp", fmt.Sprintf(":%d", *serverBasePort), DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second)) 150 | if err != nil { 151 | return nil, err 152 | } 153 | c.Do("FLUSHDB") 154 | return c, nil 155 | } 156 | 157 | func TestMain(m *testing.M) { 158 | os.Exit(func() int { 159 | flag.Parse() 160 | 161 | var f *os.File 162 | if *serverLogName != "" { 163 | var err error 164 | f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600) 165 | if err != nil { 166 | fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err) 167 | return 1 168 | } 169 | defer f.Close() 170 | serverLog = f 171 | } 172 | 173 | defer stopDefaultServer() 174 | 175 | return m.Run() 176 | }()) 177 | } 178 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/server.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "net" 7 | "os" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // Server configuration 13 | type Server struct { 14 | config *Config 15 | info *ServerInfo 16 | commands map[string]Handler 17 | 18 | tcp, unix net.Listener 19 | clients *clients 20 | } 21 | 22 | // NewServer creates a new server instance 23 | func NewServer(config *Config) *Server { 24 | if config == nil { 25 | config = DefaultConfig 26 | } 27 | 28 | clients := newClientRegistry() 29 | return &Server{ 30 | config: config, 31 | clients: clients, 32 | info: newServerInfo(config, clients), 33 | commands: make(map[string]Handler), 34 | } 35 | } 36 | 37 | // Addr returns the server TCP address 38 | func (srv *Server) Addr() string { 39 | return srv.config.Addr 40 | } 41 | 42 | // Socket returns the server UNIX socket address 43 | func (srv *Server) Socket() string { 44 | return srv.config.Socket 45 | } 46 | 47 | // Info returns the server info registry 48 | func (srv *Server) Info() *ServerInfo { 49 | return srv.info 50 | } 51 | 52 | // Close shuts down the server and closes all connections 53 | func (srv *Server) Close() (err error) { 54 | 55 | // Stop new TCP connections 56 | if srv.tcp != nil { 57 | if e := srv.tcp.Close(); e != nil { 58 | err = e 59 | } 60 | srv.tcp = nil 61 | } 62 | 63 | // Stop new Unix socket connections 64 | if srv.unix != nil { 65 | if e := srv.unix.Close(); e != nil { 66 | err = e 67 | } 68 | srv.unix = nil 69 | } 70 | 71 | // Terminate all clients 72 | if e := srv.clients.Clear(); err != nil { 73 | err = e 74 | } 75 | 76 | return 77 | } 78 | 79 | // Handle registers a handler for a command. 80 | // Not thread-safe, don't call from multiple goroutines 81 | func (srv *Server) Handle(name string, handler Handler) { 82 | srv.commands[strings.ToLower(name)] = handler 83 | } 84 | 85 | // HandleFunc registers a handler callback for a command 86 | func (srv *Server) HandleFunc(name string, callback HandlerFunc) { 87 | srv.Handle(name, Handler(callback)) 88 | } 89 | 90 | // ListenAndServe starts the server 91 | func (srv *Server) ListenAndServe() (err error) { 92 | errs := make(chan error, 2) 93 | 94 | if srv.Addr() != "" { 95 | srv.tcp, err = net.Listen("tcp", srv.Addr()) 96 | if err != nil { 97 | return 98 | } 99 | go func() { errs <- srv.Serve(srv.tcp) }() 100 | } 101 | 102 | if srv.Socket() != "" { 103 | srv.unix, err = srv.listenUnix() 104 | if err != nil { 105 | return err 106 | } 107 | go func() { errs <- srv.Serve(srv.unix) }() 108 | } 109 | 110 | return <-errs 111 | } 112 | 113 | // ------------------------------------------------------------------------ 114 | 115 | // Applies a request. Returns true when we should continue the client connection 116 | func (srv *Server) apply(req *Request, w io.Writer) bool { 117 | res := NewResponder(w) 118 | cmd, ok := srv.commands[req.Name] 119 | if !ok { 120 | res.WriteError(UnknownCommand(req.Name)) 121 | _ = res.release() 122 | return true 123 | } 124 | 125 | srv.info.onCommand() 126 | if req.client != nil { 127 | req.client.trackCommand(req.Name) 128 | } 129 | 130 | err := cmd.ServeClient(res, req) 131 | if res.buf.Len() == 0 { 132 | if err != nil { 133 | res.WriteError(err) 134 | } else { 135 | res.WriteOK() 136 | } 137 | } 138 | return res.release() == nil 139 | } 140 | 141 | // Serve accepts incoming connections on a listener, creating a 142 | // new service goroutine for each. 143 | func (srv *Server) Serve(lis net.Listener) error { 144 | defer lis.Close() 145 | 146 | for { 147 | conn, err := lis.Accept() 148 | if err != nil { 149 | return err 150 | } 151 | go srv.serveClient(NewClient(conn)) 152 | } 153 | } 154 | 155 | // Starts a new session, serving client 156 | func (srv *Server) serveClient(client *Client) { 157 | // Register client 158 | srv.clients.Put(client) 159 | defer srv.clients.Close(client.id) 160 | 161 | // Track connection 162 | srv.info.onConnect() 163 | 164 | // Apply TCP keep-alive, if configured 165 | if alive := srv.config.TCPKeepAlive; alive > 0 { 166 | if tcpconn, ok := client.conn.(*net.TCPConn); ok { 167 | tcpconn.SetKeepAlive(true) 168 | tcpconn.SetKeepAlivePeriod(alive) 169 | } 170 | } 171 | 172 | // Init request/response loop 173 | reader := bufio.NewReader(client.conn) 174 | for { 175 | if timeout := srv.config.Timeout; timeout > 0 { 176 | client.conn.SetDeadline(time.Now().Add(timeout)) 177 | } 178 | 179 | req, err := ParseRequest(reader) 180 | if err != nil { 181 | NewResponder(client.conn).WriteError(err) 182 | return 183 | } 184 | req.client = client 185 | 186 | ok := srv.apply(req, client.conn) 187 | if !ok || client.quit { 188 | return 189 | } 190 | } 191 | } 192 | 193 | // listenUnix starts the unix listener on socket path 194 | func (srv *Server) listenUnix() (net.Listener, error) { 195 | if stat, err := os.Stat(srv.Socket()); !os.IsNotExist(err) && !stat.IsDir() { 196 | if err = os.RemoveAll(srv.Socket()); err != nil { 197 | return nil, err 198 | } 199 | } 200 | return net.Listen("unix", srv.Socket()) 201 | } 202 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/redis.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strings" 7 | "sync" 8 | "time" 9 | 10 | "github.com/bsm/redeo" 11 | ) 12 | 13 | const ( 14 | msgWrongType = "WRONGTYPE Operation against a key holding the wrong kind of value" 15 | msgInvalidInt = "ERR value is not an integer or out of range" 16 | msgInvalidFloat = "ERR value is not a valid float" 17 | msgInvalidMinMax = "ERR min or max is not a float" 18 | msgInvalidRangeItem = "ERR min or max not valid string range item" 19 | msgInvalidTimeout = "ERR timeout is not an integer or out of range" 20 | msgSyntaxError = "ERR syntax error" 21 | msgKeyNotFound = "ERR no such key" 22 | msgOutOfRange = "ERR index out of range" 23 | msgInvalidCursor = "ERR invalid cursor" 24 | msgXXandNX = "ERR XX and NX options at the same time are not compatible" 25 | msgNegTimeout = "ERR timeout is negative" 26 | ) 27 | 28 | // withTx wraps the non-argument-checking part of command handling code in 29 | // transaction logic. 30 | func withTx( 31 | m *Miniredis, 32 | out *redeo.Responder, 33 | r *redeo.Request, 34 | cb txCmd, 35 | ) error { 36 | ctx := getCtx(r.Client()) 37 | if inTx(ctx) { 38 | addTxCmd(ctx, cb) 39 | out.WriteInlineString("QUEUED") 40 | return nil 41 | } 42 | m.Lock() 43 | cb(out, ctx) 44 | // done, wake up anyone who waits on anything. 45 | m.signal.Broadcast() 46 | m.Unlock() 47 | return nil 48 | } 49 | 50 | // blockCmd is executed returns whether it is done 51 | type blockCmd func(*redeo.Responder, *connCtx) bool 52 | 53 | // blocking keeps trying a command until the callback returns true. Calls 54 | // onTimeout after the timeout (or when we call this in a transaction). 55 | func blocking( 56 | m *Miniredis, 57 | out *redeo.Responder, 58 | r *redeo.Request, 59 | timeout time.Duration, 60 | cb blockCmd, 61 | onTimeout func(out *redeo.Responder), 62 | ) { 63 | var ( 64 | ctx = getCtx(r.Client()) 65 | dl *time.Timer 66 | dlc <-chan time.Time 67 | ) 68 | if inTx(ctx) { 69 | addTxCmd(ctx, func(out *redeo.Responder, ctx *connCtx) { 70 | if !cb(out, ctx) { 71 | onTimeout(out) 72 | } 73 | }) 74 | out.WriteInlineString("QUEUED") 75 | return 76 | } 77 | if timeout != 0 { 78 | dl = time.NewTimer(timeout) 79 | defer dl.Stop() 80 | dlc = dl.C 81 | } 82 | 83 | m.Lock() 84 | defer m.Unlock() 85 | for { 86 | done := cb(out, ctx) 87 | if done { 88 | return 89 | } 90 | // there is no cond.WaitTimeout(), so hence the the goroutine to wait 91 | // for a timeout 92 | var ( 93 | wg sync.WaitGroup 94 | wakeup = make(chan struct{}, 1) 95 | ) 96 | wg.Add(1) 97 | go func() { 98 | m.signal.Wait() 99 | wakeup <- struct{}{} 100 | wg.Done() 101 | }() 102 | select { 103 | case <-wakeup: 104 | case <-dlc: 105 | onTimeout(out) 106 | m.signal.Broadcast() // to kill the wakeup go routine 107 | wg.Wait() 108 | return 109 | } 110 | wg.Wait() 111 | } 112 | } 113 | 114 | // formatFloat formats a float the way redis does (sort-of) 115 | func formatFloat(v float64) string { 116 | // Format with %f and strip trailing 0s. This is the most like Redis does 117 | // it :( 118 | // .12 is the magic number where most output is the same as Redis. 119 | if math.IsInf(v, +1) { 120 | return "inf" 121 | } 122 | if math.IsInf(v, -1) { 123 | return "-inf" 124 | } 125 | sv := fmt.Sprintf("%.12f", v) 126 | for strings.Contains(sv, ".") { 127 | if sv[len(sv)-1] != '0' { 128 | break 129 | } 130 | // Remove trailing 0s. 131 | sv = sv[:len(sv)-1] 132 | // Ends with a '.'. 133 | if sv[len(sv)-1] == '.' { 134 | sv = sv[:len(sv)-1] 135 | break 136 | } 137 | } 138 | return sv 139 | } 140 | 141 | // redisRange gives Go offsets for something l long with start/end in 142 | // Redis semantics. Both start and end can be negative. 143 | // Used for string range and list range things. 144 | // The results can be used as: v[start:end] 145 | // Note that GETRANGE (on a string key) never returns an empty string when end 146 | // is a large negative number. 147 | func redisRange(l, start, end int, stringSymantics bool) (int, int) { 148 | if start < 0 { 149 | start = l + start 150 | if start < 0 { 151 | start = 0 152 | } 153 | } 154 | if start > l { 155 | start = l 156 | } 157 | 158 | if end < 0 { 159 | end = l + end 160 | if end < 0 { 161 | end = -1 162 | if stringSymantics { 163 | end = 0 164 | } 165 | } 166 | } 167 | end++ // end argument is inclusive in Redis. 168 | if end > l { 169 | end = l 170 | } 171 | 172 | if end < start { 173 | return 0, 0 174 | } 175 | return start, end 176 | } 177 | 178 | // matchKeys filters only matching keys. 179 | // Will return an empty list on invalid match expression. 180 | func matchKeys(keys []string, match string) []string { 181 | re := patternRE(match) 182 | if re == nil { 183 | // Special case, the given pattern won't match anything / is 184 | // invalid. 185 | return nil 186 | } 187 | res := []string{} 188 | for _, k := range keys { 189 | if !re.MatchString(k) { 190 | continue 191 | } 192 | res = append(res, k) 193 | } 194 | return res 195 | } 196 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/responder_test.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "strings" 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | var _ = Describe("Responder", func() { 14 | var subject *Responder 15 | var out bytes.Buffer 16 | 17 | BeforeEach(func() { 18 | out = bytes.Buffer{} 19 | subject = NewResponder(&out) 20 | }) 21 | 22 | It("should mark as failed when a write fails", func() { 23 | subject = NewResponder(&badWriter{}) 24 | subject.WriteOK() 25 | Expect(subject.Flush()).To(HaveOccurred()) 26 | }) 27 | 28 | It("should write inline strings", func() { 29 | subject.WriteInlineString("HELLO") 30 | Expect(subject.Flush()).NotTo(HaveOccurred()) 31 | Expect(out.String()).To(Equal("+HELLO\r\n")) 32 | }) 33 | 34 | It("should write strings", func() { 35 | subject.WriteString("HELLO") 36 | Expect(subject.Flush()).NotTo(HaveOccurred()) 37 | Expect(out.String()).To(Equal("$5\r\nHELLO\r\n")) 38 | }) 39 | 40 | It("should write string slices", func() { 41 | subject.WriteStringBulk([]string{"A", "", "CD"}) 42 | Expect(subject.Flush()).NotTo(HaveOccurred()) 43 | Expect(out.String()).To(Equal("*3\r\n$1\r\nA\r\n$0\r\n\r\n$2\r\nCD\r\n")) 44 | }) 45 | 46 | It("should write plain bytes", func() { 47 | subject.WriteBytes([]byte("HELLO")) 48 | Expect(subject.Flush()).NotTo(HaveOccurred()) 49 | Expect(out.String()).To(Equal("$5\r\nHELLO\r\n")) 50 | }) 51 | 52 | It("should write byte slices", func() { 53 | subject.WriteBulk([][]byte{{'A'}, nil, {'C', 'D'}}) 54 | Expect(subject.Flush()).NotTo(HaveOccurred()) 55 | Expect(out.String()).To(Equal("*3\r\n$1\r\nA\r\n$-1\r\n$2\r\nCD\r\n")) 56 | }) 57 | 58 | It("should write ints", func() { 59 | subject.WriteInt(345) 60 | subject.WriteZero() 61 | subject.WriteOne() 62 | Expect(subject.Flush()).NotTo(HaveOccurred()) 63 | Expect(out.String()).To(Equal(":345\r\n:0\r\n:1\r\n")) 64 | }) 65 | 66 | It("should write error strings", func() { 67 | subject.WriteErrorString("ERR some error") 68 | Expect(subject.Flush()).NotTo(HaveOccurred()) 69 | Expect(out.String()).To(Equal("-ERR some error\r\n")) 70 | }) 71 | 72 | It("should write errors", func() { 73 | subject.WriteError(ErrInvalidRequest) 74 | Expect(subject.Flush()).NotTo(HaveOccurred()) 75 | Expect(out.String()).To(Equal("-ERR invalid request\r\n")) 76 | }) 77 | 78 | It("should write OK", func() { 79 | subject.WriteOK() 80 | Expect(subject.Flush()).NotTo(HaveOccurred()) 81 | Expect(out.String()).To(Equal("+OK\r\n")) 82 | }) 83 | 84 | It("should write nils", func() { 85 | subject.WriteNil() 86 | Expect(subject.Flush()).NotTo(HaveOccurred()) 87 | Expect(out.String()).To(Equal("$-1\r\n")) 88 | }) 89 | 90 | It("should write bulk lens", func() { 91 | subject.WriteBulkLen(4) 92 | Expect(subject.Flush()).NotTo(HaveOccurred()) 93 | Expect(out.String()).To(Equal("*4\r\n")) 94 | }) 95 | 96 | It("should stream data", func() { 97 | subject.WriteN(strings.NewReader("HELLO STREAM"), 9) 98 | Expect(subject.Flush()).NotTo(HaveOccurred()) 99 | Expect(out.String()).To(Equal("$9\r\nHELLO STR\r\n")) 100 | }) 101 | 102 | It("should stream data with prefix and suffix", func() { 103 | subject.WriteNil() 104 | subject.WriteN(strings.NewReader("ECHOX"), 4) 105 | subject.WriteOK() 106 | Expect(subject.Flush()).NotTo(HaveOccurred()) 107 | Expect(out.String()).To(Equal("$-1\r\n$4\r\nECHO\r\n+OK\r\n")) 108 | }) 109 | 110 | }) 111 | 112 | func BenchmarkResponder_WriteOK(b *testing.B) { 113 | r := NewResponder(ioutil.Discard) 114 | for i := 0; i < b.N; i++ { 115 | r.WriteOK() 116 | } 117 | r.Flush() 118 | } 119 | 120 | func BenchmarkResponder_WriteNil(b *testing.B) { 121 | r := NewResponder(ioutil.Discard) 122 | for i := 0; i < b.N; i++ { 123 | r.WriteNil() 124 | } 125 | r.Flush() 126 | } 127 | 128 | func BenchmarkResponder_WriteInlineString(b *testing.B) { 129 | r := NewResponder(ioutil.Discard) 130 | s := strings.Repeat("x", 64) 131 | b.ResetTimer() 132 | for i := 0; i < b.N; i++ { 133 | r.WriteInlineString(s) 134 | } 135 | r.Flush() 136 | } 137 | 138 | func BenchmarkResponder_WriteString(b *testing.B) { 139 | r := NewResponder(ioutil.Discard) 140 | s := strings.Repeat("x", 64) 141 | b.ResetTimer() 142 | for i := 0; i < b.N; i++ { 143 | r.WriteString(s) 144 | } 145 | r.Flush() 146 | } 147 | 148 | func BenchmarkResponder_WriteBytes(b *testing.B) { 149 | r := NewResponder(ioutil.Discard) 150 | p := bytes.Repeat([]byte{'x'}, 64) 151 | b.ResetTimer() 152 | for i := 0; i < b.N; i++ { 153 | r.WriteBytes(p) 154 | } 155 | r.Flush() 156 | } 157 | 158 | func BenchmarkResponder_WriteStringBulks(b *testing.B) { 159 | r := NewResponder(ioutil.Discard) 160 | s := strings.Repeat("x", 64) 161 | t := []string{s, s, s, s, s} 162 | b.ResetTimer() 163 | for i := 0; i < b.N; i++ { 164 | r.WriteStringBulk(t) 165 | } 166 | r.Flush() 167 | } 168 | 169 | func BenchmarkResponder_WriteBulk(b *testing.B) { 170 | r := NewResponder(ioutil.Discard) 171 | p := bytes.Repeat([]byte{'x'}, 64) 172 | t := [][]byte{p, nil, p, p, p} 173 | b.ResetTimer() 174 | for i := 0; i < b.N; i++ { 175 | r.WriteBulk(t) 176 | } 177 | r.Flush() 178 | } 179 | 180 | func BenchmarkResponder_WriteInt(b *testing.B) { 181 | r := NewResponder(ioutil.Discard) 182 | for i := 0; i < b.N; i++ { 183 | r.WriteInt(98765) 184 | } 185 | r.Flush() 186 | } 187 | -------------------------------------------------------------------------------- /vendor/github.com/bsm/redeo/responder.go: -------------------------------------------------------------------------------- 1 | package redeo 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "strconv" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | const ( 12 | codeInline = '+' 13 | codeError = '-' 14 | codeFixnum = ':' 15 | codeStrLen = '$' 16 | codeBulkLen = '*' 17 | ) 18 | 19 | var ( 20 | binCRLF = []byte("\r\n") 21 | binOK = []byte("+OK\r\n") 22 | binZERO = []byte(":0\r\n") 23 | binONE = []byte(":1\r\n") 24 | binNIL = []byte("$-1\r\n") 25 | ) 26 | 27 | var bufferPool sync.Pool 28 | 29 | // Responder generates client responses 30 | type Responder struct { 31 | w io.Writer 32 | 33 | buf *bytes.Buffer 34 | err error 35 | } 36 | 37 | // NewResponder creates a new responder instance 38 | func NewResponder(w io.Writer) *Responder { 39 | var buf *bytes.Buffer 40 | if v := bufferPool.Get(); v != nil { 41 | buf = v.(*bytes.Buffer) 42 | buf.Reset() 43 | } else { 44 | buf = new(bytes.Buffer) 45 | } 46 | 47 | return &Responder{w: w, buf: buf} 48 | } 49 | 50 | // WriteBulkLen writes a bulk length 51 | func (r *Responder) WriteBulkLen(n int) { 52 | r.writeInline(codeBulkLen, strconv.Itoa(n)) 53 | } 54 | 55 | // WriteBulk writes a slice 56 | func (r *Responder) WriteBulk(bulk [][]byte) { 57 | if r.err != nil { 58 | return 59 | } 60 | 61 | r.WriteBulkLen(len(bulk)) 62 | for _, b := range bulk { 63 | if b == nil { 64 | r.WriteNil() 65 | } else { 66 | r.WriteBytes(b) 67 | } 68 | } 69 | } 70 | 71 | // WriteStringBulk writes a string slice 72 | func (r *Responder) WriteStringBulk(bulk []string) { 73 | if r.err != nil { 74 | return 75 | } 76 | 77 | r.WriteBulkLen(len(bulk)) 78 | for _, b := range bulk { 79 | r.WriteString(b) 80 | } 81 | } 82 | 83 | // WriteString writes a bulk string 84 | func (r *Responder) WriteString(s string) { 85 | if r.err != nil { 86 | return 87 | } 88 | 89 | if err := r.buf.WriteByte(codeStrLen); err != nil { 90 | r.err = err 91 | return 92 | } 93 | if _, err := r.buf.WriteString(strconv.Itoa(len(s))); err != nil { 94 | r.err = err 95 | return 96 | } 97 | if _, err := r.buf.Write(binCRLF); err != nil { 98 | r.err = err 99 | return 100 | } 101 | if _, err := r.buf.WriteString(s); err != nil { 102 | r.err = err 103 | return 104 | } 105 | if _, err := r.buf.Write(binCRLF); err != nil { 106 | r.err = err 107 | return 108 | } 109 | } 110 | 111 | // WriteBytes writes a bulk string 112 | func (r *Responder) WriteBytes(b []byte) { 113 | if r.err != nil { 114 | return 115 | } 116 | 117 | if err := r.buf.WriteByte(codeStrLen); err != nil { 118 | r.err = err 119 | return 120 | } 121 | if _, err := r.buf.WriteString(strconv.Itoa(len(b))); err != nil { 122 | r.err = err 123 | return 124 | } 125 | if _, err := r.buf.Write(binCRLF); err != nil { 126 | r.err = err 127 | return 128 | } 129 | if _, err := r.buf.Write(b); err != nil { 130 | r.err = err 131 | return 132 | } 133 | if _, err := r.buf.Write(binCRLF); err != nil { 134 | r.err = err 135 | return 136 | } 137 | } 138 | 139 | // WriteString writes an inline string 140 | func (r *Responder) WriteInlineString(s string) { 141 | r.writeInline(codeInline, s) 142 | } 143 | 144 | // WriteNil writes a nil value 145 | func (r *Responder) WriteNil() { 146 | r.writeRaw(binNIL) 147 | } 148 | 149 | // WriteOK writes OK 150 | func (r *Responder) WriteOK() { 151 | r.writeRaw(binOK) 152 | } 153 | 154 | // WriteInt writes an inline integer 155 | func (r *Responder) WriteInt(n int) { 156 | r.writeInline(codeFixnum, strconv.Itoa(n)) 157 | } 158 | 159 | // WriteZero writes a 0 integer 160 | func (r *Responder) WriteZero() { 161 | r.writeRaw(binZERO) 162 | } 163 | 164 | // WriteOne writes a 1 integer 165 | func (r *Responder) WriteOne() { 166 | r.writeRaw(binONE) 167 | } 168 | 169 | // WriteErrorString writes an error string 170 | func (r *Responder) WriteErrorString(s string) { 171 | r.writeInline(codeError, s) 172 | } 173 | 174 | // WriteError writes an error using the standard "ERR message" format 175 | func (r *Responder) WriteError(err error) { 176 | s := err.Error() 177 | if i := strings.LastIndex(s, ": "); i > -1 { 178 | s = s[i+2:] 179 | } 180 | r.WriteErrorString("ERR " + s) 181 | } 182 | 183 | // WriteN streams data from a reader 184 | func (r *Responder) WriteN(rd io.Reader, n int64) { 185 | if r.err != nil { 186 | return 187 | } 188 | 189 | if err := r.buf.WriteByte(codeStrLen); err != nil { 190 | r.err = err 191 | return 192 | } 193 | if _, err := r.buf.WriteString(strconv.FormatInt(n, 10)); err != nil { 194 | r.err = err 195 | return 196 | } 197 | if _, err := r.buf.Write(binCRLF); err != nil { 198 | r.err = err 199 | return 200 | } 201 | if err := r.Flush(); err != nil { 202 | return 203 | } 204 | if _, err := io.CopyN(r.w, rd, n); err != nil { 205 | r.err = err 206 | return 207 | } 208 | if _, err := r.buf.Write(binCRLF); err != nil { 209 | r.err = err 210 | return 211 | } 212 | } 213 | 214 | func (r *Responder) Flush() error { 215 | if r.err == nil { 216 | _, r.err = io.Copy(r.w, r.buf) 217 | r.buf.Reset() 218 | } 219 | return r.err 220 | } 221 | 222 | // ------------------------------------------------------------------------ 223 | 224 | func (r *Responder) release() error { 225 | err := r.Flush() 226 | bufferPool.Put(r.buf) 227 | return err 228 | } 229 | 230 | func (r *Responder) writeInline(prefix byte, s string) { 231 | if r.err != nil { 232 | return 233 | } 234 | 235 | if err := r.buf.WriteByte(prefix); err != nil { 236 | r.err = err 237 | return 238 | } 239 | if _, err := r.buf.WriteString(s); err != nil { 240 | r.err = err 241 | return 242 | } 243 | if _, err := r.buf.Write(binCRLF); err != nil { 244 | r.err = err 245 | return 246 | } 247 | } 248 | 249 | func (r *Responder) writeRaw(p []byte) { 250 | if r.err != nil { 251 | return 252 | } 253 | if _, err := r.buf.Write(p); err != nil { 254 | r.err = err 255 | return 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/README.md: -------------------------------------------------------------------------------- 1 | # Miniredis 2 | 3 | Pure Go Redis test server, used in Go unittests. 4 | 5 | 6 | ## 7 | 8 | Sometimes you want to test code which uses Redis, without making it a full-blown 9 | integration test. 10 | Miniredis implements (parts of) the Redis server, to be used in unittests. It 11 | enables a simple, cheap, in-memory, Redis replacement, with a real TCP interface. Think of it as the Redis version of `net/http/httptest`. 12 | 13 | It saves you from using mock code, and since the redis server lives in the 14 | test process you can query for values directly, without going through the server 15 | stack. 16 | 17 | There are no dependencies on external binaries, so you can easily integrate it in automated build processes. 18 | 19 | 20 | ## Commands 21 | 22 | Implemented commands: 23 | 24 | - Connection (complete) 25 | - AUTH -- see RequireAuth() 26 | - ECHO 27 | - PING 28 | - SELECT 29 | - QUIT 30 | - Key 31 | - DEL 32 | - EXISTS 33 | - EXPIRE 34 | - EXPIREAT 35 | - KEYS 36 | - MOVE 37 | - PERSIST 38 | - PEXPIRE 39 | - PEXPIREAT 40 | - PTTL 41 | - RENAME 42 | - RENAMENX 43 | - RANDOMKEY -- call math.rand.Seed(...) once before using. 44 | - TTL 45 | - TYPE 46 | - SCAN 47 | - Transactions (complete) 48 | - DISCARD 49 | - EXEC 50 | - MULTI 51 | - UNWATCH 52 | - WATCH 53 | - Server 54 | - DBSIZE 55 | - FLUSHALL 56 | - FLUSHDB 57 | - String keys (complete) 58 | - APPEND 59 | - BITCOUNT 60 | - BITOP 61 | - BITPOS 62 | - DECR 63 | - DECRBY 64 | - GET 65 | - GETBIT 66 | - GETRANGE 67 | - GETSET 68 | - INCR 69 | - INCRBY 70 | - INCRBYFLOAT 71 | - MGET 72 | - MSET 73 | - MSETNX 74 | - PSETEX 75 | - SET 76 | - SETBIT 77 | - SETEX 78 | - SETNX 79 | - SETRANGE 80 | - STRLEN 81 | - Hash keys (complete) 82 | - HDEL 83 | - HEXISTS 84 | - HGET 85 | - HGETALL 86 | - HINCRBY 87 | - HINCRBYFLOAT 88 | - HKEYS 89 | - HLEN 90 | - HMGET 91 | - HMSET 92 | - HSET 93 | - HSETNX 94 | - HVALS 95 | - HSCAN 96 | - List keys (complete) 97 | - BLPOP 98 | - BRPOP 99 | - BRPOPLPUSH 100 | - LINDEX 101 | - LINSERT 102 | - LLEN 103 | - LPOP 104 | - LPUSH 105 | - LPUSHX 106 | - LRANGE 107 | - LREM 108 | - LSET 109 | - LTRIM 110 | - RPOP 111 | - RPOPLPUSH 112 | - RPUSH 113 | - RPUSHX 114 | - Set keys (complete) 115 | - SADD 116 | - SCARD 117 | - SDIFF 118 | - SDIFFSTORE 119 | - SINTER 120 | - SINTERSTORE 121 | - SISMEMBER 122 | - SMEMBERS 123 | - SMOVE 124 | - SPOP -- call math.rand.Seed(...) once before using. 125 | - SRANDMEMBER -- call math.rand.Seed(...) once before using. 126 | - SREM 127 | - SUNION 128 | - SUNIONSTORE 129 | - SSCAN 130 | - Sorted Set keys (complete) 131 | - ZADD 132 | - ZCARD 133 | - ZCOUNT 134 | - ZINCRBY 135 | - ZINTERSTORE 136 | - ZLEXCOUNT 137 | - ZRANGE 138 | - ZRANGEBYLEX 139 | - ZRANGEBYSCORE 140 | - ZRANK 141 | - ZREM 142 | - ZREMRANGEBYLEX 143 | - ZREMRANGEBYRANK 144 | - ZREMRANGEBYSCORE 145 | - ZREVRANGE 146 | - ZREVRANGEBYSCORE 147 | - ZREVRANK 148 | - ZSCORE 149 | - ZUNIONSTORE 150 | - ZSCAN 151 | 152 | 153 | Since miniredis is intended to be used in unittests timeouts are not implemented. 154 | You can use `Expire()` to see if an expiration is set. The value returned will 155 | be that what the client set, without any interpretation. This is to keep things 156 | testable. 157 | 158 | ## Example 159 | 160 | ``` Go 161 | func TestSomething(t *testing.T) { 162 | s, err := miniredis.Run() 163 | if err != nil { 164 | panic(err) 165 | } 166 | defer s.Close() 167 | 168 | // Optionally set some keys your code expects: 169 | s.Set("foo", "bar") 170 | s.HSet("some", "other", "key") 171 | 172 | // Run your code and see if it behaves. 173 | // An example using the redigo library from "github.com/garyburd/redigo/redis": 174 | c, err := redis.Dial("tcp", s.Addr()) 175 | _, err = c.Do("SET", "foo", "bar") 176 | 177 | // Optionally check values in redis... 178 | if got, err := s.Get("foo"); err != nil || got != "bar" { 179 | t.Error("'foo' has the wrong value") 180 | } 181 | // ... or use a helper for that: 182 | s.CheckGet(t, "foo", "bar") 183 | } 184 | ``` 185 | 186 | ## Not supported 187 | 188 | Commands which will probably not be implemented: 189 | 190 | - CLUSTER (all) 191 | - ~~CLUSTER *~~ 192 | - ~~READONLY~~ 193 | - ~~READWRITE~~ 194 | - GEO (all) -- unless someone needs these 195 | - ~~GEOADD~~ 196 | - ~~GEODIST~~ 197 | - ~~GEOHASH~~ 198 | - ~~GEOPOS~~ 199 | - ~~GEORADIUS~~ 200 | - ~~GEORADIUSBYMEMBER~~ 201 | - HyperLogLog (all) -- unless someone needs these 202 | - ~~PFADD~~ 203 | - ~~PFCOUNT~~ 204 | - ~~PFMERGE~~ 205 | - Key 206 | - ~~DUMP~~ 207 | - ~~MIGRATE~~ 208 | - ~~OBJECT~~ 209 | - ~~RESTORE~~ 210 | - ~~WAIT~~ 211 | - Pub/Sub (all) 212 | - ~~PSUBSCRIBE~~ 213 | - ~~PUBLISH~~ 214 | - ~~PUBSUB~~ 215 | - ~~PUNSUBSCRIBE~~ 216 | - ~~SUBSCRIBE~~ 217 | - ~~UNSUBSCRIBE~~ 218 | - Scripting (all) 219 | - ~~EVAL~~ 220 | - ~~EVALSHA~~ 221 | - ~~SCRIPT *~~ 222 | - Server 223 | - ~~BGSAVE~~ 224 | - ~~BGWRITEAOF~~ 225 | - ~~CLIENT *~~ 226 | - ~~COMMAND *~~ 227 | - ~~CONFIG *~~ 228 | - ~~DEBUG *~~ 229 | - ~~INFO~~ 230 | - ~~LASTSAVE~~ 231 | - ~~MONITOR~~ 232 | - ~~ROLE~~ 233 | - ~~SAVE~~ 234 | - ~~SHUTDOWN~~ 235 | - ~~SLAVEOF~~ 236 | - ~~SLOWLOG~~ 237 | - ~~SYNC~~ 238 | - ~~TIME~~ 239 | 240 | 241 | ## &c. 242 | 243 | See https://github.com/alicebob/miniredis_vs_redis for tests comparing 244 | miniredis against the real thing. Tests are run against Redis 3.2.1 (Debian). 245 | 246 | 247 | [![Build Status](https://travis-ci.org/alicebob/miniredis.svg?branch=master)](https://travis-ci.org/alicebob/miniredis) 248 | [![GoDoc](https://godoc.org/github.com/alicebob/miniredis?status.svg)](https://godoc.org/github.com/alicebob/miniredis) 249 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redisx/connmux_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redisx_test 16 | 17 | import ( 18 | "net/textproto" 19 | "sync" 20 | "testing" 21 | 22 | "github.com/garyburd/redigo/internal/redistest" 23 | "github.com/garyburd/redigo/redis" 24 | "github.com/garyburd/redigo/redisx" 25 | ) 26 | 27 | func TestConnMux(t *testing.T) { 28 | c, err := redistest.Dial() 29 | if err != nil { 30 | t.Fatalf("error connection to database, %v", err) 31 | } 32 | m := redisx.NewConnMux(c) 33 | defer m.Close() 34 | 35 | c1 := m.Get() 36 | c2 := m.Get() 37 | c1.Send("ECHO", "hello") 38 | c2.Send("ECHO", "world") 39 | c1.Flush() 40 | c2.Flush() 41 | s, err := redis.String(c1.Receive()) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | if s != "hello" { 46 | t.Fatalf("echo returned %q, want %q", s, "hello") 47 | } 48 | s, err = redis.String(c2.Receive()) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | if s != "world" { 53 | t.Fatalf("echo returned %q, want %q", s, "world") 54 | } 55 | c1.Close() 56 | c2.Close() 57 | } 58 | 59 | func TestConnMuxClose(t *testing.T) { 60 | c, err := redistest.Dial() 61 | if err != nil { 62 | t.Fatalf("error connection to database, %v", err) 63 | } 64 | m := redisx.NewConnMux(c) 65 | defer m.Close() 66 | 67 | c1 := m.Get() 68 | c2 := m.Get() 69 | 70 | if err := c1.Send("ECHO", "hello"); err != nil { 71 | t.Fatal(err) 72 | } 73 | if err := c1.Close(); err != nil { 74 | t.Fatal(err) 75 | } 76 | 77 | if err := c2.Send("ECHO", "world"); err != nil { 78 | t.Fatal(err) 79 | } 80 | if err := c2.Flush(); err != nil { 81 | t.Fatal(err) 82 | } 83 | 84 | s, err := redis.String(c2.Receive()) 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | if s != "world" { 89 | t.Fatalf("echo returned %q, want %q", s, "world") 90 | } 91 | c2.Close() 92 | } 93 | 94 | func BenchmarkConn(b *testing.B) { 95 | b.StopTimer() 96 | c, err := redistest.Dial() 97 | if err != nil { 98 | b.Fatalf("error connection to database, %v", err) 99 | } 100 | defer c.Close() 101 | b.StartTimer() 102 | 103 | for i := 0; i < b.N; i++ { 104 | if _, err := c.Do("PING"); err != nil { 105 | b.Fatal(err) 106 | } 107 | } 108 | } 109 | 110 | func BenchmarkConnMux(b *testing.B) { 111 | b.StopTimer() 112 | c, err := redistest.Dial() 113 | if err != nil { 114 | b.Fatalf("error connection to database, %v", err) 115 | } 116 | m := redisx.NewConnMux(c) 117 | defer m.Close() 118 | 119 | b.StartTimer() 120 | 121 | for i := 0; i < b.N; i++ { 122 | c := m.Get() 123 | if _, err := c.Do("PING"); err != nil { 124 | b.Fatal(err) 125 | } 126 | c.Close() 127 | } 128 | } 129 | 130 | func BenchmarkPool(b *testing.B) { 131 | b.StopTimer() 132 | 133 | p := redis.Pool{Dial: redistest.Dial, MaxIdle: 1} 134 | defer p.Close() 135 | 136 | // Fill the pool. 137 | c := p.Get() 138 | if err := c.Err(); err != nil { 139 | b.Fatal(err) 140 | } 141 | c.Close() 142 | 143 | b.StartTimer() 144 | 145 | for i := 0; i < b.N; i++ { 146 | c := p.Get() 147 | if _, err := c.Do("PING"); err != nil { 148 | b.Fatal(err) 149 | } 150 | c.Close() 151 | } 152 | } 153 | 154 | const numConcurrent = 10 155 | 156 | func BenchmarkConnMuxConcurrent(b *testing.B) { 157 | b.StopTimer() 158 | c, err := redistest.Dial() 159 | if err != nil { 160 | b.Fatalf("error connection to database, %v", err) 161 | } 162 | defer c.Close() 163 | 164 | m := redisx.NewConnMux(c) 165 | 166 | var wg sync.WaitGroup 167 | wg.Add(numConcurrent) 168 | 169 | b.StartTimer() 170 | 171 | for i := 0; i < numConcurrent; i++ { 172 | go func() { 173 | defer wg.Done() 174 | for i := 0; i < b.N; i++ { 175 | c := m.Get() 176 | if _, err := c.Do("PING"); err != nil { 177 | b.Fatal(err) 178 | } 179 | c.Close() 180 | } 181 | }() 182 | } 183 | wg.Wait() 184 | } 185 | 186 | func BenchmarkPoolConcurrent(b *testing.B) { 187 | b.StopTimer() 188 | 189 | p := redis.Pool{Dial: redistest.Dial, MaxIdle: numConcurrent} 190 | defer p.Close() 191 | 192 | // Fill the pool. 193 | conns := make([]redis.Conn, numConcurrent) 194 | for i := range conns { 195 | c := p.Get() 196 | if err := c.Err(); err != nil { 197 | b.Fatal(err) 198 | } 199 | conns[i] = c 200 | } 201 | for _, c := range conns { 202 | c.Close() 203 | } 204 | 205 | var wg sync.WaitGroup 206 | wg.Add(numConcurrent) 207 | 208 | b.StartTimer() 209 | 210 | for i := 0; i < numConcurrent; i++ { 211 | go func() { 212 | defer wg.Done() 213 | for i := 0; i < b.N; i++ { 214 | c := p.Get() 215 | if _, err := c.Do("PING"); err != nil { 216 | b.Fatal(err) 217 | } 218 | c.Close() 219 | } 220 | }() 221 | } 222 | wg.Wait() 223 | } 224 | 225 | func BenchmarkPipelineConcurrency(b *testing.B) { 226 | b.StopTimer() 227 | c, err := redistest.Dial() 228 | if err != nil { 229 | b.Fatalf("error connection to database, %v", err) 230 | } 231 | defer c.Close() 232 | 233 | var wg sync.WaitGroup 234 | wg.Add(numConcurrent) 235 | 236 | var pipeline textproto.Pipeline 237 | 238 | b.StartTimer() 239 | 240 | for i := 0; i < numConcurrent; i++ { 241 | go func() { 242 | defer wg.Done() 243 | for i := 0; i < b.N; i++ { 244 | id := pipeline.Next() 245 | pipeline.StartRequest(id) 246 | c.Send("PING") 247 | c.Flush() 248 | pipeline.EndRequest(id) 249 | pipeline.StartResponse(id) 250 | _, err := c.Receive() 251 | if err != nil { 252 | b.Fatal(err) 253 | } 254 | pipeline.EndResponse(id) 255 | } 256 | }() 257 | } 258 | wg.Wait() 259 | } 260 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/cmd_transactions_test.go: -------------------------------------------------------------------------------- 1 | package miniredis 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/garyburd/redigo/redis" 7 | ) 8 | 9 | func TestMulti(t *testing.T) { 10 | s, err := Run() 11 | ok(t, err) 12 | defer s.Close() 13 | c, err := redis.Dial("tcp", s.Addr()) 14 | ok(t, err) 15 | 16 | // Do accept MULTI, but use it as a no-op 17 | r, err := redis.String(c.Do("MULTI")) 18 | ok(t, err) 19 | equals(t, "OK", r) 20 | } 21 | 22 | func TestExec(t *testing.T) { 23 | s, err := Run() 24 | ok(t, err) 25 | defer s.Close() 26 | c, err := redis.Dial("tcp", s.Addr()) 27 | ok(t, err) 28 | 29 | // Exec without MULTI. 30 | _, err = c.Do("EXEC") 31 | assert(t, err != nil, "do EXEC error") 32 | } 33 | 34 | func TestDiscard(t *testing.T) { 35 | s, err := Run() 36 | ok(t, err) 37 | defer s.Close() 38 | c, err := redis.Dial("tcp", s.Addr()) 39 | ok(t, err) 40 | 41 | // DISCARD without MULTI. 42 | _, err = c.Do("DISCARD") 43 | assert(t, err != nil, "do DISCARD error") 44 | } 45 | 46 | func TestWatch(t *testing.T) { 47 | s, err := Run() 48 | ok(t, err) 49 | defer s.Close() 50 | c, err := redis.Dial("tcp", s.Addr()) 51 | ok(t, err) 52 | 53 | // Simple WATCH 54 | r, err := redis.String(c.Do("WATCH", "foo")) 55 | ok(t, err) 56 | equals(t, "OK", r) 57 | 58 | // Can't do WATCH in a MULTI 59 | { 60 | _, err = redis.String(c.Do("MULTI")) 61 | ok(t, err) 62 | _, err = redis.String(c.Do("WATCH", "foo")) 63 | assert(t, err != nil, "do WATCH error") 64 | } 65 | } 66 | 67 | // Test simple multi/exec block. 68 | func TestSimpleTransaction(t *testing.T) { 69 | s, err := Run() 70 | ok(t, err) 71 | defer s.Close() 72 | c, err := redis.Dial("tcp", s.Addr()) 73 | ok(t, err) 74 | 75 | b, err := redis.String(c.Do("MULTI")) 76 | ok(t, err) 77 | equals(t, "OK", b) 78 | 79 | b, err = redis.String(c.Do("SET", "aap", 1)) 80 | ok(t, err) 81 | equals(t, "QUEUED", b) 82 | 83 | // Not set yet. 84 | equals(t, false, s.Exists("aap")) 85 | 86 | v, err := redis.Values(c.Do("EXEC")) 87 | ok(t, err) 88 | equals(t, 1, len(redis.Args(v))) 89 | equals(t, "OK", v[0]) 90 | 91 | // SET should be back to normal mode 92 | b, err = redis.String(c.Do("SET", "aap", 1)) 93 | ok(t, err) 94 | equals(t, "OK", b) 95 | } 96 | 97 | func TestDiscardTransaction(t *testing.T) { 98 | s, err := Run() 99 | ok(t, err) 100 | defer s.Close() 101 | c, err := redis.Dial("tcp", s.Addr()) 102 | ok(t, err) 103 | 104 | s.Set("aap", "noot") 105 | 106 | b, err := redis.String(c.Do("MULTI")) 107 | ok(t, err) 108 | equals(t, "OK", b) 109 | 110 | b, err = redis.String(c.Do("SET", "aap", "mies")) 111 | ok(t, err) 112 | equals(t, "QUEUED", b) 113 | 114 | // Not committed 115 | s.CheckGet(t, "aap", "noot") 116 | 117 | v, err := redis.String(c.Do("DISCARD")) 118 | ok(t, err) 119 | equals(t, "OK", v) 120 | 121 | // TX didn't get executed 122 | s.CheckGet(t, "aap", "noot") 123 | } 124 | 125 | func TestTxQueueErr(t *testing.T) { 126 | s, err := Run() 127 | ok(t, err) 128 | defer s.Close() 129 | c, err := redis.Dial("tcp", s.Addr()) 130 | ok(t, err) 131 | 132 | b, err := redis.String(c.Do("MULTI")) 133 | ok(t, err) 134 | equals(t, "OK", b) 135 | 136 | b, err = redis.String(c.Do("SET", "aap", "mies")) 137 | ok(t, err) 138 | equals(t, "QUEUED", b) 139 | 140 | // That's an error! 141 | _, err = redis.String(c.Do("SET", "aap")) 142 | assert(t, err != nil, "do SET error") 143 | 144 | // Thisone is ok again 145 | b, err = redis.String(c.Do("SET", "noot", "vuur")) 146 | ok(t, err) 147 | equals(t, "QUEUED", b) 148 | 149 | _, err = redis.String(c.Do("EXEC")) 150 | assert(t, err != nil, "do EXEC error") 151 | 152 | // Didn't get EXECed 153 | equals(t, false, s.Exists("aap")) 154 | } 155 | 156 | func TestTxWatch(t *testing.T) { 157 | // Watch with no error. 158 | s, err := Run() 159 | ok(t, err) 160 | defer s.Close() 161 | c, err := redis.Dial("tcp", s.Addr()) 162 | ok(t, err) 163 | 164 | s.Set("one", "two") 165 | b, err := redis.String(c.Do("WATCH", "one")) 166 | ok(t, err) 167 | equals(t, "OK", b) 168 | 169 | b, err = redis.String(c.Do("MULTI")) 170 | ok(t, err) 171 | equals(t, "OK", b) 172 | 173 | b, err = redis.String(c.Do("GET", "one")) 174 | ok(t, err) 175 | equals(t, "QUEUED", b) 176 | 177 | v, err := redis.Values(c.Do("EXEC")) 178 | ok(t, err) 179 | equals(t, 1, len(v)) 180 | equals(t, []byte("two"), v[0]) 181 | } 182 | 183 | func TestTxWatchErr(t *testing.T) { 184 | // Watch with en error. 185 | s, err := Run() 186 | ok(t, err) 187 | defer s.Close() 188 | c, err := redis.Dial("tcp", s.Addr()) 189 | ok(t, err) 190 | c2, err := redis.Dial("tcp", s.Addr()) 191 | ok(t, err) 192 | 193 | s.Set("one", "two") 194 | b, err := redis.String(c.Do("WATCH", "one")) 195 | ok(t, err) 196 | equals(t, "OK", b) 197 | 198 | // Here comes client 2 199 | b, err = redis.String(c2.Do("SET", "one", "three")) 200 | ok(t, err) 201 | equals(t, "OK", b) 202 | 203 | b, err = redis.String(c.Do("MULTI")) 204 | ok(t, err) 205 | equals(t, "OK", b) 206 | 207 | b, err = redis.String(c.Do("GET", "one")) 208 | ok(t, err) 209 | equals(t, "QUEUED", b) 210 | 211 | v, err := redis.Values(c.Do("EXEC")) 212 | ok(t, err) 213 | equals(t, 0, len(v)) 214 | 215 | // It did get updated, and we're not in a transaction anymore. 216 | b, err = redis.String(c.Do("GET", "one")) 217 | ok(t, err) 218 | equals(t, "three", b) 219 | } 220 | 221 | func TestUnwatch(t *testing.T) { 222 | s, err := Run() 223 | ok(t, err) 224 | defer s.Close() 225 | c, err := redis.Dial("tcp", s.Addr()) 226 | ok(t, err) 227 | c2, err := redis.Dial("tcp", s.Addr()) 228 | ok(t, err) 229 | 230 | s.Set("one", "two") 231 | b, err := redis.String(c.Do("WATCH", "one")) 232 | ok(t, err) 233 | equals(t, "OK", b) 234 | 235 | b, err = redis.String(c.Do("UNWATCH")) 236 | ok(t, err) 237 | equals(t, "OK", b) 238 | 239 | // Here comes client 2 240 | b, err = redis.String(c2.Do("SET", "one", "three")) 241 | ok(t, err) 242 | equals(t, "OK", b) 243 | 244 | b, err = redis.String(c.Do("MULTI")) 245 | ok(t, err) 246 | equals(t, "OK", b) 247 | 248 | b, err = redis.String(c.Do("SET", "one", "four")) 249 | ok(t, err) 250 | equals(t, "QUEUED", b) 251 | 252 | v, err := redis.Values(c.Do("EXEC")) 253 | ok(t, err) 254 | equals(t, 1, len(v)) 255 | equals(t, "OK", v[0]) 256 | 257 | // It did get updated by our TX 258 | b, err = redis.String(c.Do("GET", "one")) 259 | ok(t, err) 260 | equals(t, "four", b) 261 | } 262 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Package redis is a client for the Redis database. 16 | // 17 | // The Redigo FAQ (https://github.com/garyburd/redigo/wiki/FAQ) contains more 18 | // documentation about this package. 19 | // 20 | // Connections 21 | // 22 | // The Conn interface is the primary interface for working with Redis. 23 | // Applications create connections by calling the Dial, DialWithTimeout or 24 | // NewConn functions. In the future, functions will be added for creating 25 | // sharded and other types of connections. 26 | // 27 | // The application must call the connection Close method when the application 28 | // is done with the connection. 29 | // 30 | // Executing Commands 31 | // 32 | // The Conn interface has a generic method for executing Redis commands: 33 | // 34 | // Do(commandName string, args ...interface{}) (reply interface{}, err error) 35 | // 36 | // The Redis command reference (http://redis.io/commands) lists the available 37 | // commands. An example of using the Redis APPEND command is: 38 | // 39 | // n, err := conn.Do("APPEND", "key", "value") 40 | // 41 | // The Do method converts command arguments to binary strings for transmission 42 | // to the server as follows: 43 | // 44 | // Go Type Conversion 45 | // []byte Sent as is 46 | // string Sent as is 47 | // int, int64 strconv.FormatInt(v) 48 | // float64 strconv.FormatFloat(v, 'g', -1, 64) 49 | // bool true -> "1", false -> "0" 50 | // nil "" 51 | // all other types fmt.Print(v) 52 | // 53 | // Redis command reply types are represented using the following Go types: 54 | // 55 | // Redis type Go type 56 | // error redis.Error 57 | // integer int64 58 | // simple string string 59 | // bulk string []byte or nil if value not present. 60 | // array []interface{} or nil if value not present. 61 | // 62 | // Use type assertions or the reply helper functions to convert from 63 | // interface{} to the specific Go type for the command result. 64 | // 65 | // Pipelining 66 | // 67 | // Connections support pipelining using the Send, Flush and Receive methods. 68 | // 69 | // Send(commandName string, args ...interface{}) error 70 | // Flush() error 71 | // Receive() (reply interface{}, err error) 72 | // 73 | // Send writes the command to the connection's output buffer. Flush flushes the 74 | // connection's output buffer to the server. Receive reads a single reply from 75 | // the server. The following example shows a simple pipeline. 76 | // 77 | // c.Send("SET", "foo", "bar") 78 | // c.Send("GET", "foo") 79 | // c.Flush() 80 | // c.Receive() // reply from SET 81 | // v, err = c.Receive() // reply from GET 82 | // 83 | // The Do method combines the functionality of the Send, Flush and Receive 84 | // methods. The Do method starts by writing the command and flushing the output 85 | // buffer. Next, the Do method receives all pending replies including the reply 86 | // for the command just sent by Do. If any of the received replies is an error, 87 | // then Do returns the error. If there are no errors, then Do returns the last 88 | // reply. If the command argument to the Do method is "", then the Do method 89 | // will flush the output buffer and receive pending replies without sending a 90 | // command. 91 | // 92 | // Use the Send and Do methods to implement pipelined transactions. 93 | // 94 | // c.Send("MULTI") 95 | // c.Send("INCR", "foo") 96 | // c.Send("INCR", "bar") 97 | // r, err := c.Do("EXEC") 98 | // fmt.Println(r) // prints [1, 1] 99 | // 100 | // Concurrency 101 | // 102 | // Connections do not support concurrent calls to the write methods (Send, 103 | // Flush) or concurrent calls to the read method (Receive). Connections do 104 | // allow a concurrent reader and writer. 105 | // 106 | // Because the Do method combines the functionality of Send, Flush and Receive, 107 | // the Do method cannot be called concurrently with the other methods. 108 | // 109 | // For full concurrent access to Redis, use the thread-safe Pool to get and 110 | // release connections from within a goroutine. 111 | // 112 | // Publish and Subscribe 113 | // 114 | // Use the Send, Flush and Receive methods to implement Pub/Sub subscribers. 115 | // 116 | // c.Send("SUBSCRIBE", "example") 117 | // c.Flush() 118 | // for { 119 | // reply, err := c.Receive() 120 | // if err != nil { 121 | // return err 122 | // } 123 | // // process pushed message 124 | // } 125 | // 126 | // The PubSubConn type wraps a Conn with convenience methods for implementing 127 | // subscribers. The Subscribe, PSubscribe, Unsubscribe and PUnsubscribe methods 128 | // send and flush a subscription management command. The receive method 129 | // converts a pushed message to convenient types for use in a type switch. 130 | // 131 | // psc := redis.PubSubConn{c} 132 | // psc.Subscribe("example") 133 | // for { 134 | // switch v := psc.Receive().(type) { 135 | // case redis.Message: 136 | // fmt.Printf("%s: message: %s\n", v.Channel, v.Data) 137 | // case redis.Subscription: 138 | // fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count) 139 | // case error: 140 | // return v 141 | // } 142 | // } 143 | // 144 | // Reply Helpers 145 | // 146 | // The Bool, Int, Bytes, String, Strings and Values functions convert a reply 147 | // to a value of a specific type. To allow convenient wrapping of calls to the 148 | // connection Do and Receive methods, the functions take a second argument of 149 | // type error. If the error is non-nil, then the helper function returns the 150 | // error. If the error is nil, the function converts the reply to the specified 151 | // type: 152 | // 153 | // exists, err := redis.Bool(c.Do("EXISTS", "foo")) 154 | // if err != nil { 155 | // // handle error return from c.Do or type conversion error. 156 | // } 157 | // 158 | // The Scan function converts elements of a array reply to Go types: 159 | // 160 | // var value1 int 161 | // var value2 string 162 | // reply, err := redis.Values(c.Do("MGET", "key1", "key2")) 163 | // if err != nil { 164 | // // handle error 165 | // } 166 | // if _, err := redis.Scan(reply, &value1, &value2); err != nil { 167 | // // handle error 168 | // } 169 | package redis // import "github.com/garyburd/redigo/redis" 170 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/miniredis.go: -------------------------------------------------------------------------------- 1 | // Package miniredis is a pure Go Redis test server, for use in Go unittests. 2 | // There are no dependencies on system binaries, and every server you start 3 | // will be empty. 4 | // 5 | // Start a server with `s, err := miniredis.Run()`. 6 | // Stop it with `defer s.Close()`. 7 | // 8 | // Point your Redis client to `s.Addr()` or `s.Host(), s.Port()`. 9 | // 10 | // Set keys directly via s.Set(...) and similar commands, or use a Redis client. 11 | // 12 | // For direct use you can select a Redis database with either `s.Select(12); 13 | // s.Get("foo")` or `s.DB(12).Get("foo")`. 14 | // 15 | package miniredis 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "sync" 21 | 22 | "github.com/bsm/redeo" 23 | ) 24 | 25 | type hashKey map[string]string 26 | type listKey []string 27 | type setKey map[string]struct{} 28 | 29 | // RedisDB holds a single (numbered) Redis database. 30 | type RedisDB struct { 31 | master *sync.Mutex // pointer to the lock in Miniredis 32 | id int // db id 33 | keys map[string]string // Master map of keys with their type 34 | stringKeys map[string]string // GET/SET &c. keys 35 | hashKeys map[string]hashKey // MGET/MSET &c. keys 36 | listKeys map[string]listKey // LPUSH &c. keys 37 | setKeys map[string]setKey // SADD &c. keys 38 | sortedsetKeys map[string]sortedSet // ZADD &c. keys 39 | expire map[string]int // EXPIRE values 40 | keyVersion map[string]uint // used to watch values 41 | } 42 | 43 | // Miniredis is a Redis server implementation. 44 | type Miniredis struct { 45 | sync.Mutex 46 | srv *redeo.Server 47 | listenAddr string 48 | password string 49 | closed chan struct{} 50 | listen net.Listener 51 | dbs map[int]*RedisDB 52 | selectedDB int // DB id used in the direct Get(), Set() &c. 53 | signal *sync.Cond 54 | } 55 | 56 | type txCmd func(*redeo.Responder, *connCtx) 57 | 58 | // database id + key combo 59 | type dbKey struct { 60 | db int 61 | key string 62 | } 63 | 64 | // connCtx has all state for a single connection. 65 | type connCtx struct { 66 | selectedDB int // selected DB 67 | authenticated bool // auth enabled and a valid AUTH seen 68 | transaction []txCmd // transaction callbacks. Or nil. 69 | dirtyTransaction bool // any error during QUEUEing. 70 | watch map[dbKey]uint // WATCHed keys. 71 | } 72 | 73 | // NewMiniRedis makes a new, non-started, Miniredis object. 74 | func NewMiniRedis() *Miniredis { 75 | m := Miniredis{ 76 | closed: make(chan struct{}), 77 | dbs: map[int]*RedisDB{}, 78 | } 79 | m.signal = sync.NewCond(&m) 80 | return &m 81 | } 82 | 83 | func newRedisDB(id int, l *sync.Mutex) RedisDB { 84 | return RedisDB{ 85 | id: id, 86 | master: l, 87 | keys: map[string]string{}, 88 | stringKeys: map[string]string{}, 89 | hashKeys: map[string]hashKey{}, 90 | listKeys: map[string]listKey{}, 91 | setKeys: map[string]setKey{}, 92 | sortedsetKeys: map[string]sortedSet{}, 93 | expire: map[string]int{}, 94 | keyVersion: map[string]uint{}, 95 | } 96 | } 97 | 98 | // Run creates and Start()s a Miniredis. 99 | func Run() (*Miniredis, error) { 100 | m := NewMiniRedis() 101 | return m, m.Start() 102 | } 103 | 104 | // Start starts a server. It listens on a random port on localhost. See also 105 | // Addr(). 106 | func (m *Miniredis) Start() error { 107 | m.Lock() 108 | defer m.Unlock() 109 | 110 | l, err := listen("127.0.0.1:0") 111 | if err != nil { 112 | return err 113 | } 114 | m.listen = l 115 | m.listenAddr = l.Addr().String() 116 | m.srv = redeo.NewServer(&redeo.Config{Addr: m.listenAddr}) 117 | 118 | commandsConnection(m, m.srv) 119 | commandsGeneric(m, m.srv) 120 | commandsServer(m, m.srv) 121 | commandsString(m, m.srv) 122 | commandsHash(m, m.srv) 123 | commandsList(m, m.srv) 124 | commandsSet(m, m.srv) 125 | commandsSortedSet(m, m.srv) 126 | commandsTransaction(m, m.srv) 127 | 128 | go func() { 129 | m.srv.Serve(m.listen) 130 | m.closed <- struct{}{} 131 | }() 132 | return nil 133 | } 134 | 135 | // Restart restarts a Close()d server on the same port. Values will be 136 | // preserved. 137 | func (m *Miniredis) Restart() error { 138 | m.Lock() 139 | defer m.Unlock() 140 | 141 | l, err := listen(m.listenAddr) 142 | if err != nil { 143 | return err 144 | } 145 | m.listen = l 146 | 147 | go func() { 148 | m.srv.Serve(m.listen) 149 | m.closed <- struct{}{} 150 | }() 151 | 152 | return nil 153 | } 154 | 155 | func listen(addr string) (net.Listener, error) { 156 | l, err := net.Listen("tcp", addr) 157 | if err != nil { 158 | if l, err = net.Listen("tcp6", addr); err != nil { 159 | return nil, fmt.Errorf("failed to listen on a port: %v", err) 160 | } 161 | } 162 | return l, nil 163 | } 164 | 165 | // Close shuts down a Miniredis. 166 | func (m *Miniredis) Close() { 167 | m.Lock() 168 | defer m.Unlock() 169 | if m.listen == nil { 170 | return 171 | } 172 | if m.listen.Close() != nil { 173 | return 174 | } 175 | m.srv.Close() 176 | <-m.closed 177 | m.listen = nil 178 | } 179 | 180 | // RequireAuth makes every connection need to AUTH first. Disable again by 181 | // setting an empty string. 182 | func (m *Miniredis) RequireAuth(pw string) { 183 | m.Lock() 184 | defer m.Unlock() 185 | m.password = pw 186 | } 187 | 188 | // DB returns a DB by ID. 189 | func (m *Miniredis) DB(i int) *RedisDB { 190 | m.Lock() 191 | defer m.Unlock() 192 | return m.db(i) 193 | } 194 | 195 | // get DB. No locks! 196 | func (m *Miniredis) db(i int) *RedisDB { 197 | if db, ok := m.dbs[i]; ok { 198 | return db 199 | } 200 | db := newRedisDB(i, &m.Mutex) // the DB has our lock. 201 | m.dbs[i] = &db 202 | return &db 203 | } 204 | 205 | // Addr returns '127.0.0.1:12345'. Can be given to a Dial(). See also Host() 206 | // and Port(), which return the same things. 207 | func (m *Miniredis) Addr() string { 208 | m.Lock() 209 | defer m.Unlock() 210 | return m.listenAddr 211 | } 212 | 213 | // Host returns the host part of Addr(). 214 | func (m *Miniredis) Host() string { 215 | m.Lock() 216 | defer m.Unlock() 217 | host, _, _ := net.SplitHostPort(m.listenAddr) 218 | return host 219 | } 220 | 221 | // Port returns the (random) port part of Addr(). 222 | func (m *Miniredis) Port() string { 223 | m.Lock() 224 | defer m.Unlock() 225 | _, port, _ := net.SplitHostPort(m.listenAddr) 226 | return port 227 | } 228 | 229 | // CommandCount returns the number of processed commands. 230 | func (m *Miniredis) CommandCount() int { 231 | m.Lock() 232 | defer m.Unlock() 233 | return int(m.srv.Info().TotalCommands()) 234 | } 235 | 236 | // CurrentConnectionCount returns the number of currently connected clients. 237 | func (m *Miniredis) CurrentConnectionCount() int { 238 | m.Lock() 239 | defer m.Unlock() 240 | return m.srv.Info().ClientsLen() 241 | } 242 | 243 | // TotalConnectionCount returns the number of client connections since server start. 244 | func (m *Miniredis) TotalConnectionCount() int { 245 | m.Lock() 246 | defer m.Unlock() 247 | return int(m.srv.Info().TotalConnections()) 248 | } 249 | 250 | // Dump returns a text version of the selected DB, usable for debugging. 251 | func (m *Miniredis) Dump() string { 252 | m.Lock() 253 | defer m.Unlock() 254 | 255 | var ( 256 | maxLen = 60 257 | indent = " " 258 | db = m.db(m.selectedDB) 259 | r = "" 260 | v = func(s string) string { 261 | suffix := "" 262 | if len(s) > maxLen { 263 | suffix = fmt.Sprintf("...(%d)", len(s)) 264 | s = s[:maxLen-len(suffix)] 265 | } 266 | return fmt.Sprintf("%q%s", s, suffix) 267 | } 268 | ) 269 | for _, k := range db.allKeys() { 270 | r += fmt.Sprintf("- %s\n", k) 271 | t := db.t(k) 272 | switch t { 273 | case "string": 274 | r += fmt.Sprintf("%s%s\n", indent, v(db.stringKeys[k])) 275 | case "hash": 276 | for _, hk := range db.hashFields(k) { 277 | r += fmt.Sprintf("%s%s: %s\n", indent, hk, v(db.hashGet(k, hk))) 278 | } 279 | case "list": 280 | for _, lk := range db.listKeys[k] { 281 | r += fmt.Sprintf("%s%s\n", indent, v(lk)) 282 | } 283 | case "set": 284 | for _, mk := range db.setMembers(k) { 285 | r += fmt.Sprintf("%s%s\n", indent, v(mk)) 286 | } 287 | case "zset": 288 | for _, el := range db.ssetElements(k) { 289 | r += fmt.Sprintf("%s%f: %s\n", indent, el.score, v(el.member)) 290 | } 291 | default: 292 | r += fmt.Sprintf("%s(a %s, fixme!)\n", indent, t) 293 | } 294 | } 295 | return r 296 | } 297 | 298 | // handleAuth returns false if connection has no access. It sends the reply. 299 | func (m *Miniredis) handleAuth(cl *redeo.Client, out *redeo.Responder) bool { 300 | m.Lock() 301 | defer m.Unlock() 302 | if m.password == "" { 303 | return true 304 | } 305 | if cl.Ctx == nil || !getCtx(cl).authenticated { 306 | out.WriteErrorString("NOAUTH Authentication required.") 307 | return false 308 | } 309 | return true 310 | } 311 | 312 | func getCtx(cl *redeo.Client) *connCtx { 313 | if cl.Ctx == nil { 314 | cl.Ctx = &connCtx{} 315 | } 316 | return cl.Ctx.(*connCtx) 317 | } 318 | 319 | func startTx(ctx *connCtx) { 320 | ctx.transaction = []txCmd{} 321 | ctx.dirtyTransaction = false 322 | } 323 | 324 | func stopTx(ctx *connCtx) { 325 | ctx.transaction = nil 326 | unwatch(ctx) 327 | } 328 | 329 | func inTx(ctx *connCtx) bool { 330 | return ctx.transaction != nil 331 | } 332 | 333 | func addTxCmd(ctx *connCtx, cb txCmd) { 334 | ctx.transaction = append(ctx.transaction, cb) 335 | } 336 | 337 | func dirtyTx(ctx *connCtx) bool { 338 | return ctx.dirtyTransaction 339 | } 340 | 341 | func watch(db *RedisDB, ctx *connCtx, key string) { 342 | if ctx.watch == nil { 343 | ctx.watch = map[dbKey]uint{} 344 | } 345 | ctx.watch[dbKey{db: db.id, key: key}] = db.keyVersion[key] // Can be 0. 346 | } 347 | 348 | func unwatch(ctx *connCtx) { 349 | ctx.watch = nil 350 | } 351 | 352 | // setDirty can be called even when not in an tx. Is an no-op then. 353 | func setDirty(cl *redeo.Client) { 354 | if cl.Ctx == nil { 355 | // No transaction. Not relevant. 356 | return 357 | } 358 | getCtx(cl).dirtyTransaction = true 359 | } 360 | 361 | func setAuthenticated(cl *redeo.Client) { 362 | getCtx(cl).authenticated = true 363 | } 364 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /vendor/github.com/alicebob/miniredis/cmd_generic.go: -------------------------------------------------------------------------------- 1 | // Commands from http://redis.io/commands#generic 2 | 3 | package miniredis 4 | 5 | import ( 6 | "math/rand" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/bsm/redeo" 11 | ) 12 | 13 | // commandsGeneric handles EXPIRE, TTL, PERSIST, &c. 14 | func commandsGeneric(m *Miniredis, srv *redeo.Server) { 15 | srv.HandleFunc("DEL", m.cmdDel) 16 | // DUMP 17 | srv.HandleFunc("EXISTS", m.cmdExists) 18 | srv.HandleFunc("EXPIRE", makeCmdExpire(m, "expire")) 19 | srv.HandleFunc("EXPIREAT", makeCmdExpire(m, "expireat")) 20 | srv.HandleFunc("KEYS", m.cmdKeys) 21 | // MIGRATE 22 | srv.HandleFunc("MOVE", m.cmdMove) 23 | // OBJECT 24 | srv.HandleFunc("PERSIST", m.cmdPersist) 25 | srv.HandleFunc("PEXPIRE", makeCmdExpire(m, "pexpire")) 26 | srv.HandleFunc("PEXPIREAT", makeCmdExpire(m, "pexpireat")) 27 | srv.HandleFunc("PTTL", m.cmdPTTL) 28 | srv.HandleFunc("RANDOMKEY", m.cmdRandomkey) 29 | srv.HandleFunc("RENAME", m.cmdRename) 30 | srv.HandleFunc("RENAMENX", m.cmdRenamenx) 31 | // RESTORE 32 | // SORT 33 | srv.HandleFunc("TTL", m.cmdTTL) 34 | srv.HandleFunc("TYPE", m.cmdType) 35 | srv.HandleFunc("SCAN", m.cmdScan) 36 | } 37 | 38 | // generic expire command for EXPIRE, PEXPIRE, EXPIREAT, PEXPIREAT 39 | func makeCmdExpire(m *Miniredis, cmd string) func(*redeo.Responder, *redeo.Request) error { 40 | return func(out *redeo.Responder, r *redeo.Request) error { 41 | if len(r.Args) != 2 { 42 | setDirty(r.Client()) 43 | return r.WrongNumberOfArgs() 44 | } 45 | if !m.handleAuth(r.Client(), out) { 46 | return nil 47 | } 48 | 49 | key := r.Args[0] 50 | value := r.Args[1] 51 | i, err := strconv.Atoi(value) 52 | if err != nil { 53 | setDirty(r.Client()) 54 | out.WriteErrorString(msgInvalidInt) 55 | return nil 56 | } 57 | 58 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 59 | db := m.db(ctx.selectedDB) 60 | 61 | // Key must be present. 62 | if _, ok := db.keys[key]; !ok { 63 | out.WriteZero() 64 | return 65 | } 66 | db.expire[key] = i 67 | db.keyVersion[key]++ 68 | out.WriteOne() 69 | }) 70 | } 71 | } 72 | 73 | // TTL 74 | func (m *Miniredis) cmdTTL(out *redeo.Responder, r *redeo.Request) error { 75 | if len(r.Args) != 1 { 76 | setDirty(r.Client()) 77 | return r.WrongNumberOfArgs() 78 | } 79 | if !m.handleAuth(r.Client(), out) { 80 | return nil 81 | } 82 | key := r.Args[0] 83 | 84 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 85 | db := m.db(ctx.selectedDB) 86 | 87 | if _, ok := db.keys[key]; !ok { 88 | // No such key 89 | out.WriteInt(-2) 90 | return 91 | } 92 | 93 | value, ok := db.expire[key] 94 | if !ok { 95 | // No expire value 96 | out.WriteInt(-1) 97 | return 98 | } 99 | out.WriteInt(value) 100 | }) 101 | } 102 | 103 | // PTTL 104 | func (m *Miniredis) cmdPTTL(out *redeo.Responder, r *redeo.Request) error { 105 | if len(r.Args) != 1 { 106 | setDirty(r.Client()) 107 | return r.WrongNumberOfArgs() 108 | } 109 | if !m.handleAuth(r.Client(), out) { 110 | return nil 111 | } 112 | key := r.Args[0] 113 | 114 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 115 | db := m.db(ctx.selectedDB) 116 | 117 | if _, ok := db.keys[key]; !ok { 118 | // No such key 119 | out.WriteInt(-2) 120 | return 121 | } 122 | 123 | value, ok := db.expire[key] 124 | if !ok { 125 | // No expire value 126 | out.WriteInt(-1) 127 | return 128 | } 129 | out.WriteInt(value) 130 | }) 131 | } 132 | 133 | // PERSIST 134 | func (m *Miniredis) cmdPersist(out *redeo.Responder, r *redeo.Request) error { 135 | if len(r.Args) != 1 { 136 | setDirty(r.Client()) 137 | return r.WrongNumberOfArgs() 138 | } 139 | if !m.handleAuth(r.Client(), out) { 140 | return nil 141 | } 142 | key := r.Args[0] 143 | 144 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 145 | db := m.db(ctx.selectedDB) 146 | 147 | if _, ok := db.keys[key]; !ok { 148 | // No such key 149 | out.WriteInt(0) 150 | return 151 | } 152 | 153 | _, ok := db.expire[key] 154 | if !ok { 155 | // No expire value 156 | out.WriteInt(0) 157 | return 158 | } 159 | delete(db.expire, key) 160 | db.keyVersion[key]++ 161 | out.WriteInt(1) 162 | }) 163 | } 164 | 165 | // DEL 166 | func (m *Miniredis) cmdDel(out *redeo.Responder, r *redeo.Request) error { 167 | if !m.handleAuth(r.Client(), out) { 168 | return nil 169 | } 170 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 171 | db := m.db(ctx.selectedDB) 172 | 173 | count := 0 174 | for _, key := range r.Args { 175 | if db.exists(key) { 176 | count++ 177 | } 178 | db.del(key, true) // delete expire 179 | } 180 | out.WriteInt(count) 181 | }) 182 | } 183 | 184 | // TYPE 185 | func (m *Miniredis) cmdType(out *redeo.Responder, r *redeo.Request) error { 186 | if len(r.Args) != 1 { 187 | setDirty(r.Client()) 188 | out.WriteErrorString("usage error") 189 | return nil 190 | } 191 | if !m.handleAuth(r.Client(), out) { 192 | return nil 193 | } 194 | 195 | key := r.Args[0] 196 | 197 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 198 | db := m.db(ctx.selectedDB) 199 | 200 | t, ok := db.keys[key] 201 | if !ok { 202 | out.WriteInlineString("none") 203 | return 204 | } 205 | 206 | out.WriteInlineString(t) 207 | }) 208 | } 209 | 210 | // EXISTS 211 | func (m *Miniredis) cmdExists(out *redeo.Responder, r *redeo.Request) error { 212 | if len(r.Args) < 1 { 213 | setDirty(r.Client()) 214 | return r.WrongNumberOfArgs() 215 | } 216 | if !m.handleAuth(r.Client(), out) { 217 | return nil 218 | } 219 | 220 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 221 | db := m.db(ctx.selectedDB) 222 | 223 | found := 0 224 | for _, k := range r.Args { 225 | if db.exists(k) { 226 | found++ 227 | } 228 | } 229 | out.WriteInt(found) 230 | }) 231 | } 232 | 233 | // MOVE 234 | func (m *Miniredis) cmdMove(out *redeo.Responder, r *redeo.Request) error { 235 | if len(r.Args) != 2 { 236 | setDirty(r.Client()) 237 | return r.WrongNumberOfArgs() 238 | } 239 | if !m.handleAuth(r.Client(), out) { 240 | return nil 241 | } 242 | 243 | key := r.Args[0] 244 | targetDB, err := strconv.Atoi(r.Args[1]) 245 | if err != nil { 246 | targetDB = 0 247 | } 248 | 249 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 250 | if ctx.selectedDB == targetDB { 251 | out.WriteErrorString("ERR source and destination objects are the same") 252 | return 253 | } 254 | db := m.db(ctx.selectedDB) 255 | targetDB := m.db(targetDB) 256 | 257 | if !db.move(key, targetDB) { 258 | out.WriteZero() 259 | return 260 | } 261 | out.WriteOne() 262 | }) 263 | } 264 | 265 | // KEYS 266 | func (m *Miniredis) cmdKeys(out *redeo.Responder, r *redeo.Request) error { 267 | if len(r.Args) != 1 { 268 | setDirty(r.Client()) 269 | return r.WrongNumberOfArgs() 270 | } 271 | if !m.handleAuth(r.Client(), out) { 272 | return nil 273 | } 274 | 275 | key := r.Args[0] 276 | 277 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 278 | db := m.db(ctx.selectedDB) 279 | 280 | keys := matchKeys(db.allKeys(), key) 281 | out.WriteBulkLen(len(keys)) 282 | for _, s := range keys { 283 | out.WriteString(s) 284 | } 285 | }) 286 | } 287 | 288 | // RANDOMKEY 289 | func (m *Miniredis) cmdRandomkey(out *redeo.Responder, r *redeo.Request) error { 290 | if len(r.Args) != 0 { 291 | setDirty(r.Client()) 292 | return r.WrongNumberOfArgs() 293 | } 294 | if !m.handleAuth(r.Client(), out) { 295 | return nil 296 | } 297 | 298 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 299 | db := m.db(ctx.selectedDB) 300 | 301 | if len(db.keys) == 0 { 302 | out.WriteNil() 303 | return 304 | } 305 | nr := rand.Intn(len(db.keys)) 306 | for k := range db.keys { 307 | if nr == 0 { 308 | out.WriteString(k) 309 | return 310 | } 311 | nr-- 312 | } 313 | }) 314 | } 315 | 316 | // RENAME 317 | func (m *Miniredis) cmdRename(out *redeo.Responder, r *redeo.Request) error { 318 | if len(r.Args) != 2 { 319 | setDirty(r.Client()) 320 | return r.WrongNumberOfArgs() 321 | } 322 | if !m.handleAuth(r.Client(), out) { 323 | return nil 324 | } 325 | 326 | from := r.Args[0] 327 | to := r.Args[1] 328 | 329 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 330 | db := m.db(ctx.selectedDB) 331 | 332 | if !db.exists(from) { 333 | out.WriteErrorString(msgKeyNotFound) 334 | return 335 | } 336 | 337 | db.rename(from, to) 338 | out.WriteOK() 339 | }) 340 | } 341 | 342 | // RENAMENX 343 | func (m *Miniredis) cmdRenamenx(out *redeo.Responder, r *redeo.Request) error { 344 | if len(r.Args) != 2 { 345 | setDirty(r.Client()) 346 | return r.WrongNumberOfArgs() 347 | } 348 | if !m.handleAuth(r.Client(), out) { 349 | return nil 350 | } 351 | 352 | from := r.Args[0] 353 | to := r.Args[1] 354 | 355 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 356 | db := m.db(ctx.selectedDB) 357 | 358 | if !db.exists(from) { 359 | out.WriteErrorString(msgKeyNotFound) 360 | return 361 | } 362 | 363 | if db.exists(to) { 364 | out.WriteZero() 365 | return 366 | } 367 | 368 | db.rename(from, to) 369 | out.WriteOne() 370 | }) 371 | } 372 | 373 | // SCAN 374 | func (m *Miniredis) cmdScan(out *redeo.Responder, r *redeo.Request) error { 375 | if len(r.Args) < 1 { 376 | setDirty(r.Client()) 377 | return r.WrongNumberOfArgs() 378 | } 379 | if !m.handleAuth(r.Client(), out) { 380 | return nil 381 | } 382 | 383 | cursor, err := strconv.Atoi(r.Args[0]) 384 | if err != nil { 385 | setDirty(r.Client()) 386 | out.WriteErrorString(msgInvalidCursor) 387 | return nil 388 | } 389 | // MATCH and COUNT options 390 | var withMatch bool 391 | var match string 392 | args := r.Args[1:] 393 | for len(args) > 0 { 394 | if strings.ToLower(args[0]) == "count" { 395 | if len(args) < 2 { 396 | setDirty(r.Client()) 397 | out.WriteErrorString(msgSyntaxError) 398 | return nil 399 | } 400 | _, err := strconv.Atoi(args[1]) 401 | if err != nil { 402 | setDirty(r.Client()) 403 | out.WriteErrorString(msgInvalidInt) 404 | return nil 405 | } 406 | // We do nothing with count. 407 | args = args[2:] 408 | continue 409 | } 410 | if strings.ToLower(args[0]) == "match" { 411 | if len(args) < 2 { 412 | setDirty(r.Client()) 413 | out.WriteErrorString(msgSyntaxError) 414 | return nil 415 | } 416 | withMatch = true 417 | match = args[1] 418 | args = args[2:] 419 | continue 420 | } 421 | setDirty(r.Client()) 422 | out.WriteErrorString(msgSyntaxError) 423 | return nil 424 | } 425 | 426 | return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { 427 | db := m.db(ctx.selectedDB) 428 | // We return _all_ (matched) keys every time. 429 | 430 | if cursor != 0 { 431 | // Invalid cursor. 432 | out.WriteBulkLen(2) 433 | out.WriteString("0") // no next cursor 434 | out.WriteBulkLen(0) // no elements 435 | return 436 | } 437 | 438 | keys := db.allKeys() 439 | if withMatch { 440 | keys = matchKeys(keys, match) 441 | } 442 | 443 | out.WriteBulkLen(2) 444 | out.WriteString("0") // no next cursor 445 | out.WriteBulkLen(len(keys)) 446 | for _, k := range keys { 447 | out.WriteString(k) 448 | } 449 | }) 450 | } 451 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "bytes" 19 | "container/list" 20 | "crypto/rand" 21 | "crypto/sha1" 22 | "errors" 23 | "io" 24 | "strconv" 25 | "sync" 26 | "time" 27 | 28 | "github.com/garyburd/redigo/internal" 29 | ) 30 | 31 | var nowFunc = time.Now // for testing 32 | 33 | // ErrPoolExhausted is returned from a pool connection method (Do, Send, 34 | // Receive, Flush, Err) when the maximum number of database connections in the 35 | // pool has been reached. 36 | var ErrPoolExhausted = errors.New("redigo: connection pool exhausted") 37 | 38 | var ( 39 | errPoolClosed = errors.New("redigo: connection pool closed") 40 | errConnClosed = errors.New("redigo: connection closed") 41 | ) 42 | 43 | // Pool maintains a pool of connections. The application calls the Get method 44 | // to get a connection from the pool and the connection's Close method to 45 | // return the connection's resources to the pool. 46 | // 47 | // The following example shows how to use a pool in a web application. The 48 | // application creates a pool at application startup and makes it available to 49 | // request handlers using a global variable. 50 | // 51 | // func newPool(server, password string) *redis.Pool { 52 | // return &redis.Pool{ 53 | // MaxIdle: 3, 54 | // IdleTimeout: 240 * time.Second, 55 | // Dial: func () (redis.Conn, error) { 56 | // c, err := redis.Dial("tcp", server) 57 | // if err != nil { 58 | // return nil, err 59 | // } 60 | // if _, err := c.Do("AUTH", password); err != nil { 61 | // c.Close() 62 | // return nil, err 63 | // } 64 | // return c, err 65 | // }, 66 | // TestOnBorrow: func(c redis.Conn, t time.Time) error { 67 | // _, err := c.Do("PING") 68 | // return err 69 | // }, 70 | // } 71 | // } 72 | // 73 | // var ( 74 | // pool *redis.Pool 75 | // redisServer = flag.String("redisServer", ":6379", "") 76 | // redisPassword = flag.String("redisPassword", "", "") 77 | // ) 78 | // 79 | // func main() { 80 | // flag.Parse() 81 | // pool = newPool(*redisServer, *redisPassword) 82 | // ... 83 | // } 84 | // 85 | // A request handler gets a connection from the pool and closes the connection 86 | // when the handler is done: 87 | // 88 | // func serveHome(w http.ResponseWriter, r *http.Request) { 89 | // conn := pool.Get() 90 | // defer conn.Close() 91 | // .... 92 | // } 93 | // 94 | type Pool struct { 95 | 96 | // Dial is an application supplied function for creating and configuring a 97 | // connection. 98 | // 99 | // The connection returned from Dial must not be in a special state 100 | // (subscribed to pubsub channel, transaction started, ...). 101 | Dial func() (Conn, error) 102 | 103 | // TestOnBorrow is an optional application supplied function for checking 104 | // the health of an idle connection before the connection is used again by 105 | // the application. Argument t is the time that the connection was returned 106 | // to the pool. If the function returns an error, then the connection is 107 | // closed. 108 | TestOnBorrow func(c Conn, t time.Time) error 109 | 110 | // Maximum number of idle connections in the pool. 111 | MaxIdle int 112 | 113 | // Maximum number of connections allocated by the pool at a given time. 114 | // When zero, there is no limit on the number of connections in the pool. 115 | MaxActive int 116 | 117 | // Close connections after remaining idle for this duration. If the value 118 | // is zero, then idle connections are not closed. Applications should set 119 | // the timeout to a value less than the server's timeout. 120 | IdleTimeout time.Duration 121 | 122 | // If Wait is true and the pool is at the MaxActive limit, then Get() waits 123 | // for a connection to be returned to the pool before returning. 124 | Wait bool 125 | 126 | // mu protects fields defined below. 127 | mu sync.Mutex 128 | cond *sync.Cond 129 | closed bool 130 | active int 131 | 132 | // Stack of idleConn with most recently used at the front. 133 | idle list.List 134 | } 135 | 136 | type idleConn struct { 137 | c Conn 138 | t time.Time 139 | } 140 | 141 | // NewPool creates a new pool. 142 | // 143 | // Deprecated: Initialize the Pool directory as shown in the example. 144 | func NewPool(newFn func() (Conn, error), maxIdle int) *Pool { 145 | return &Pool{Dial: newFn, MaxIdle: maxIdle} 146 | } 147 | 148 | // Get gets a connection. The application must close the returned connection. 149 | // This method always returns a valid connection so that applications can defer 150 | // error handling to the first use of the connection. If there is an error 151 | // getting an underlying connection, then the connection Err, Do, Send, Flush 152 | // and Receive methods return that error. 153 | func (p *Pool) Get() Conn { 154 | c, err := p.get() 155 | if err != nil { 156 | return errorConnection{err} 157 | } 158 | return &pooledConnection{p: p, c: c} 159 | } 160 | 161 | // ActiveCount returns the number of active connections in the pool. 162 | func (p *Pool) ActiveCount() int { 163 | p.mu.Lock() 164 | active := p.active 165 | p.mu.Unlock() 166 | return active 167 | } 168 | 169 | // Close releases the resources used by the pool. 170 | func (p *Pool) Close() error { 171 | p.mu.Lock() 172 | idle := p.idle 173 | p.idle.Init() 174 | p.closed = true 175 | p.active -= idle.Len() 176 | if p.cond != nil { 177 | p.cond.Broadcast() 178 | } 179 | p.mu.Unlock() 180 | for e := idle.Front(); e != nil; e = e.Next() { 181 | e.Value.(idleConn).c.Close() 182 | } 183 | return nil 184 | } 185 | 186 | // release decrements the active count and signals waiters. The caller must 187 | // hold p.mu during the call. 188 | func (p *Pool) release() { 189 | p.active -= 1 190 | if p.cond != nil { 191 | p.cond.Signal() 192 | } 193 | } 194 | 195 | // get prunes stale connections and returns a connection from the idle list or 196 | // creates a new connection. 197 | func (p *Pool) get() (Conn, error) { 198 | p.mu.Lock() 199 | 200 | // Prune stale connections. 201 | 202 | if timeout := p.IdleTimeout; timeout > 0 { 203 | for i, n := 0, p.idle.Len(); i < n; i++ { 204 | e := p.idle.Back() 205 | if e == nil { 206 | break 207 | } 208 | ic := e.Value.(idleConn) 209 | if ic.t.Add(timeout).After(nowFunc()) { 210 | break 211 | } 212 | p.idle.Remove(e) 213 | p.release() 214 | p.mu.Unlock() 215 | ic.c.Close() 216 | p.mu.Lock() 217 | } 218 | } 219 | 220 | for { 221 | 222 | // Get idle connection. 223 | 224 | for i, n := 0, p.idle.Len(); i < n; i++ { 225 | e := p.idle.Front() 226 | if e == nil { 227 | break 228 | } 229 | ic := e.Value.(idleConn) 230 | p.idle.Remove(e) 231 | test := p.TestOnBorrow 232 | p.mu.Unlock() 233 | if test == nil || test(ic.c, ic.t) == nil { 234 | return ic.c, nil 235 | } 236 | ic.c.Close() 237 | p.mu.Lock() 238 | p.release() 239 | } 240 | 241 | // Check for pool closed before dialing a new connection. 242 | 243 | if p.closed { 244 | p.mu.Unlock() 245 | return nil, errors.New("redigo: get on closed pool") 246 | } 247 | 248 | // Dial new connection if under limit. 249 | 250 | if p.MaxActive == 0 || p.active < p.MaxActive { 251 | dial := p.Dial 252 | p.active += 1 253 | p.mu.Unlock() 254 | c, err := dial() 255 | if err != nil { 256 | p.mu.Lock() 257 | p.release() 258 | p.mu.Unlock() 259 | c = nil 260 | } 261 | return c, err 262 | } 263 | 264 | if !p.Wait { 265 | p.mu.Unlock() 266 | return nil, ErrPoolExhausted 267 | } 268 | 269 | if p.cond == nil { 270 | p.cond = sync.NewCond(&p.mu) 271 | } 272 | p.cond.Wait() 273 | } 274 | } 275 | 276 | func (p *Pool) put(c Conn, forceClose bool) error { 277 | err := c.Err() 278 | p.mu.Lock() 279 | if !p.closed && err == nil && !forceClose { 280 | p.idle.PushFront(idleConn{t: nowFunc(), c: c}) 281 | if p.idle.Len() > p.MaxIdle { 282 | c = p.idle.Remove(p.idle.Back()).(idleConn).c 283 | } else { 284 | c = nil 285 | } 286 | } 287 | 288 | if c == nil { 289 | if p.cond != nil { 290 | p.cond.Signal() 291 | } 292 | p.mu.Unlock() 293 | return nil 294 | } 295 | 296 | p.release() 297 | p.mu.Unlock() 298 | return c.Close() 299 | } 300 | 301 | type pooledConnection struct { 302 | p *Pool 303 | c Conn 304 | state int 305 | } 306 | 307 | var ( 308 | sentinel []byte 309 | sentinelOnce sync.Once 310 | ) 311 | 312 | func initSentinel() { 313 | p := make([]byte, 64) 314 | if _, err := rand.Read(p); err == nil { 315 | sentinel = p 316 | } else { 317 | h := sha1.New() 318 | io.WriteString(h, "Oops, rand failed. Use time instead.") 319 | io.WriteString(h, strconv.FormatInt(time.Now().UnixNano(), 10)) 320 | sentinel = h.Sum(nil) 321 | } 322 | } 323 | 324 | func (pc *pooledConnection) Close() error { 325 | c := pc.c 326 | if _, ok := c.(errorConnection); ok { 327 | return nil 328 | } 329 | pc.c = errorConnection{errConnClosed} 330 | 331 | if pc.state&internal.MultiState != 0 { 332 | c.Send("DISCARD") 333 | pc.state &^= (internal.MultiState | internal.WatchState) 334 | } else if pc.state&internal.WatchState != 0 { 335 | c.Send("UNWATCH") 336 | pc.state &^= internal.WatchState 337 | } 338 | if pc.state&internal.SubscribeState != 0 { 339 | c.Send("UNSUBSCRIBE") 340 | c.Send("PUNSUBSCRIBE") 341 | // To detect the end of the message stream, ask the server to echo 342 | // a sentinel value and read until we see that value. 343 | sentinelOnce.Do(initSentinel) 344 | c.Send("ECHO", sentinel) 345 | c.Flush() 346 | for { 347 | p, err := c.Receive() 348 | if err != nil { 349 | break 350 | } 351 | if p, ok := p.([]byte); ok && bytes.Equal(p, sentinel) { 352 | pc.state &^= internal.SubscribeState 353 | break 354 | } 355 | } 356 | } 357 | c.Do("") 358 | pc.p.put(c, pc.state != 0) 359 | return nil 360 | } 361 | 362 | func (pc *pooledConnection) Err() error { 363 | return pc.c.Err() 364 | } 365 | 366 | func (pc *pooledConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) { 367 | ci := internal.LookupCommandInfo(commandName) 368 | pc.state = (pc.state | ci.Set) &^ ci.Clear 369 | return pc.c.Do(commandName, args...) 370 | } 371 | 372 | func (pc *pooledConnection) Send(commandName string, args ...interface{}) error { 373 | ci := internal.LookupCommandInfo(commandName) 374 | pc.state = (pc.state | ci.Set) &^ ci.Clear 375 | return pc.c.Send(commandName, args...) 376 | } 377 | 378 | func (pc *pooledConnection) Flush() error { 379 | return pc.c.Flush() 380 | } 381 | 382 | func (pc *pooledConnection) Receive() (reply interface{}, err error) { 383 | return pc.c.Receive() 384 | } 385 | 386 | type errorConnection struct{ err error } 387 | 388 | func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err } 389 | func (ec errorConnection) Send(string, ...interface{}) error { return ec.err } 390 | func (ec errorConnection) Err() error { return ec.err } 391 | func (ec errorConnection) Close() error { return ec.err } 392 | func (ec errorConnection) Flush() error { return ec.err } 393 | func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err } 394 | --------------------------------------------------------------------------------