├── bin └── .keep ├── .gitignore ├── Godeps ├── _workspace │ ├── .gitignore │ └── src │ │ └── github.com │ │ ├── boltdb │ │ └── bolt │ │ │ ├── .gitignore │ │ │ ├── bolt_386.go │ │ │ ├── bolt_arm.go │ │ │ ├── boltsync_unix.go │ │ │ ├── bolt_amd64.go │ │ │ ├── bolt_linux.go │ │ │ ├── cmd │ │ │ └── bolt │ │ │ │ ├── info.go │ │ │ │ ├── buckets.go │ │ │ │ ├── keys.go │ │ │ │ ├── get.go │ │ │ │ ├── info_test.go │ │ │ │ ├── check.go │ │ │ │ ├── buckets_test.go │ │ │ │ ├── pages.go │ │ │ │ ├── keys_test.go │ │ │ │ ├── get_test.go │ │ │ │ ├── stats_test.go │ │ │ │ ├── main_test.go │ │ │ │ ├── stats.go │ │ │ │ ├── main.go │ │ │ │ └── bench.go │ │ │ ├── bolt_openbsd.go │ │ │ ├── page_test.go │ │ │ ├── bolt_test.go │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── doc.go │ │ │ ├── bolt_windows.go │ │ │ ├── bolt_unix.go │ │ │ ├── quick_test.go │ │ │ ├── errors.go │ │ │ ├── page.go │ │ │ ├── freelist_test.go │ │ │ ├── node_test.go │ │ │ ├── freelist.go │ │ │ ├── simulation_test.go │ │ │ ├── cursor.go │ │ │ └── tx_test.go │ │ └── garyburd │ │ └── redigo │ │ ├── redis │ │ ├── test_test.go │ │ ├── redis.go │ │ ├── zpop_example_test.go │ │ ├── script_test.go │ │ ├── script.go │ │ ├── log.go │ │ ├── pubsub.go │ │ ├── reply_test.go │ │ ├── pubsub_test.go │ │ ├── doc.go │ │ ├── reply.go │ │ ├── scan_test.go │ │ ├── pool.go │ │ ├── conn.go │ │ └── conn_test.go │ │ └── internal │ │ ├── commandinfo.go │ │ └── redistest │ │ └── testdb.go ├── Readme └── Godeps.json ├── queue.go ├── Makefile ├── cmd └── httpq │ ├── redis.go │ └── main.go ├── README.md ├── LICENSE.txt ├── queue ├── redisqueue │ └── redisqueue.go └── boltqueue │ └── boltqueue.go ├── server.go └── httpq.go /bin/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | test.db 3 | httpq.db 4 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/.gitignore: -------------------------------------------------------------------------------- 1 | *.prof 2 | *.test 3 | /bin/ 4 | -------------------------------------------------------------------------------- /queue.go: -------------------------------------------------------------------------------- 1 | package httpq 2 | 3 | type Queue interface { 4 | Push([]byte) error 5 | Pop() ([]byte, error) 6 | Size() (uint64, error) 7 | } 8 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: dep 2 | go build -o bin/httpq github.com/DavidHuie/httpq/cmd/httpq 3 | 4 | test: dep 5 | go test ./... 6 | 7 | dep: 8 | godep save -r ./... 9 | 10 | install: 11 | go install github.com/DavidHuie/httpq/cmd/httpq 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/bolt_386.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | // maxMapSize represents the largest mmap size supported by Bolt. 4 | const maxMapSize = 0x7FFFFFFF // 2GB 5 | 6 | // maxAllocSize is the size used when creating array pointers. 7 | const maxAllocSize = 0xFFFFFFF 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/bolt_arm.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | // maxMapSize represents the largest mmap size supported by Bolt. 4 | const maxMapSize = 0x7FFFFFFF // 2GB 5 | 6 | // maxAllocSize is the size used when creating array pointers. 7 | const maxAllocSize = 0xFFFFFFF 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/boltsync_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!plan9,!linux,!openbsd 2 | 3 | package bolt 4 | 5 | var odirect int 6 | 7 | // fdatasync flushes written data to a file descriptor. 8 | func fdatasync(db *DB) error { 9 | return db.file.Sync() 10 | } 11 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/bolt_amd64.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | // maxMapSize represents the largest mmap size supported by Bolt. 4 | const maxMapSize = 0xFFFFFFFFFFFF // 256TB 5 | 6 | // maxAllocSize is the size used when creating array pointers. 7 | const maxAllocSize = 0x7FFFFFFF 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/bolt_linux.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | var odirect = syscall.O_DIRECT 8 | 9 | // fdatasync flushes written data to a file descriptor. 10 | func fdatasync(db *DB) error { 11 | return syscall.Fdatasync(int(db.file.Fd())) 12 | } 13 | -------------------------------------------------------------------------------- /cmd/httpq/redis.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/redis" 4 | 5 | type redisConnManager struct { 6 | url string 7 | } 8 | 9 | func NewRedisConnManager(url string) *redisConnManager { 10 | return &redisConnManager{url} 11 | } 12 | 13 | func (r *redisConnManager) newRedisConn() (redis.Conn, error) { 14 | c, err := redis.Dial("tcp", r.url) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return c, nil 19 | } 20 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/DavidHuie/httpq", 3 | "GoVersion": "go1.4.2", 4 | "Packages": [ 5 | "./..." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "github.com/boltdb/bolt", 10 | "Comment": "v1.0-43-gcf33c9e", 11 | "Rev": "cf33c9e0ca0a23509b8bb8edfc63e4776bb1a330" 12 | }, 13 | { 14 | "ImportPath": "github.com/garyburd/redigo/internal", 15 | "Rev": "535138d7bcd717d6531c701ef5933d98b1866257" 16 | }, 17 | { 18 | "ImportPath": "github.com/garyburd/redigo/redis", 19 | "Rev": "535138d7bcd717d6531c701ef5933d98b1866257" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/info.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | ) 8 | 9 | // Info prints basic information about a database. 10 | func Info(path string) { 11 | if _, err := os.Stat(path); os.IsNotExist(err) { 12 | fatal(err) 13 | return 14 | } 15 | 16 | db, err := bolt.Open(path, 0600, nil) 17 | if err != nil { 18 | fatal(err) 19 | return 20 | } 21 | defer db.Close() 22 | 23 | // Print basic database info. 24 | var info = db.Info() 25 | printf("Page Size: %d\n", info.PageSize) 26 | } 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/bolt_openbsd.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | const ( 9 | msAsync = 1 << iota // perform asynchronous writes 10 | msSync // perform synchronous writes 11 | msInvalidate // invalidate cached data 12 | ) 13 | 14 | var odirect int 15 | 16 | func msync(db *DB) error { 17 | _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(db.data)), uintptr(db.datasz), msInvalidate) 18 | if errno != 0 { 19 | return errno 20 | } 21 | return nil 22 | } 23 | 24 | func fdatasync(db *DB) error { 25 | if db.data != nil { 26 | return msync(db) 27 | } 28 | return db.file.Sync() 29 | } 30 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/buckets.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | ) 8 | 9 | // Buckets prints a list of all buckets. 10 | func Buckets(path string) { 11 | if _, err := os.Stat(path); os.IsNotExist(err) { 12 | fatal(err) 13 | return 14 | } 15 | 16 | db, err := bolt.Open(path, 0600, nil) 17 | if err != nil { 18 | fatal(err) 19 | return 20 | } 21 | defer db.Close() 22 | 23 | err = db.View(func(tx *bolt.Tx) error { 24 | return tx.ForEach(func(name []byte, _ *bolt.Bucket) error { 25 | println(string(name)) 26 | return nil 27 | }) 28 | }) 29 | if err != nil { 30 | fatal(err) 31 | return 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/keys.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | ) 8 | 9 | // Keys retrieves a list of keys for a given bucket. 10 | func Keys(path, name string) { 11 | if _, err := os.Stat(path); os.IsNotExist(err) { 12 | fatal(err) 13 | return 14 | } 15 | 16 | db, err := bolt.Open(path, 0600, nil) 17 | if err != nil { 18 | fatal(err) 19 | return 20 | } 21 | defer db.Close() 22 | 23 | err = db.View(func(tx *bolt.Tx) error { 24 | // Find bucket. 25 | b := tx.Bucket([]byte(name)) 26 | if b == nil { 27 | fatalf("bucket not found: %s", name) 28 | return nil 29 | } 30 | 31 | // Iterate over each key. 32 | return b.ForEach(func(key, _ []byte) error { 33 | println(string(key)) 34 | return nil 35 | }) 36 | }) 37 | if err != nil { 38 | fatal(err) 39 | return 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/page_test.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // Ensure that the page type can be returned in human readable format. 8 | func TestPage_typ(t *testing.T) { 9 | if typ := (&page{flags: branchPageFlag}).typ(); typ != "branch" { 10 | t.Fatalf("exp=branch; got=%v", typ) 11 | } 12 | if typ := (&page{flags: leafPageFlag}).typ(); typ != "leaf" { 13 | t.Fatalf("exp=leaf; got=%v", typ) 14 | } 15 | if typ := (&page{flags: metaPageFlag}).typ(); typ != "meta" { 16 | t.Fatalf("exp=meta; got=%v", typ) 17 | } 18 | if typ := (&page{flags: freelistPageFlag}).typ(); typ != "freelist" { 19 | t.Fatalf("exp=freelist; got=%v", typ) 20 | } 21 | if typ := (&page{flags: 20000}).typ(); typ != "unknown<4e20>" { 22 | t.Fatalf("exp=unknown<4e20>; got=%v", typ) 23 | } 24 | } 25 | 26 | // Ensure that the hexdump debugging function doesn't blow up. 27 | func TestPage_dump(t *testing.T) { 28 | (&page{id: 256}).hexdump(16) 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # httpq 2 | 3 | With Httpq, you can buffer HTTP requests and replay them later, and persistence can be either to Redis or to disk. This is useful for buffering HTTP requests that do not have to be processed in realtime, such as webhooks. 4 | 5 | ## Installation 6 | 7 | ```shell 8 | $ go install github.com/DavidHuie/httpq/cmd/httpq 9 | ``` 10 | 11 | ## Using Redis 12 | 13 | ```shell 14 | $ httpq -redis=true -redis_url=":6379" 15 | ``` 16 | 17 | ## Using disk persistence 18 | 19 | ```shell 20 | $ httpq -db_path="/tmp/httpq.db" 21 | ``` 22 | 23 | ## Queuing a request 24 | 25 | ```shell 26 | $ curl localhost:3000/push 27 | ``` 28 | 29 | ## Replaying a request 30 | 31 | ```shell 32 | $ curl localhost:3000/pop 33 | 34 | GET /push HTTP/1.1 35 | Host: localhost:3000 36 | Accept: */* 37 | User-Agent: curl/7.37.1 38 | ``` 39 | 40 | ## Determining size of queue 41 | 42 | The result is returning as JSON. 43 | 44 | ```shell 45 | $ curl localhost:3000/size 46 | 47 | {"size":3} 48 | ``` 49 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/get.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | ) 8 | 9 | // Get retrieves the value for a given bucket/key. 10 | func Get(path, name, key string) { 11 | if _, err := os.Stat(path); os.IsNotExist(err) { 12 | fatal(err) 13 | return 14 | } 15 | 16 | db, err := bolt.Open(path, 0600, nil) 17 | if err != nil { 18 | fatal(err) 19 | return 20 | } 21 | defer db.Close() 22 | 23 | err = db.View(func(tx *bolt.Tx) error { 24 | // Find bucket. 25 | b := tx.Bucket([]byte(name)) 26 | if b == nil { 27 | fatalf("bucket not found: %s", name) 28 | return nil 29 | } 30 | 31 | // Find value for a given key. 32 | value := b.Get([]byte(key)) 33 | if value == nil { 34 | fatalf("key not found: %s", key) 35 | return nil 36 | } 37 | 38 | println(string(value)) 39 | return nil 40 | }) 41 | if err != nil { 42 | fatal(err) 43 | return 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/info_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | . "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt" 8 | ) 9 | 10 | // Ensure that a database info can be printed. 11 | func TestInfo(t *testing.T) { 12 | SetTestMode(true) 13 | open(func(db *bolt.DB, path string) { 14 | db.Update(func(tx *bolt.Tx) error { 15 | tx.CreateBucket([]byte("widgets")) 16 | b := tx.Bucket([]byte("widgets")) 17 | b.Put([]byte("foo"), []byte("0000")) 18 | return nil 19 | }) 20 | db.Close() 21 | output := run("info", path) 22 | equals(t, `Page Size: 4096`, output) 23 | }) 24 | } 25 | 26 | // Ensure that an error is reported if the database is not found. 27 | func TestInfo_NotFound(t *testing.T) { 28 | SetTestMode(true) 29 | output := run("info", "no/such/db") 30 | equals(t, "stat no/such/db: no such file or directory", output) 31 | } 32 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/check.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | ) 8 | 9 | // Check performs a consistency check on the database and prints any errors found. 10 | func Check(path string) { 11 | if _, err := os.Stat(path); os.IsNotExist(err) { 12 | fatal(err) 13 | return 14 | } 15 | 16 | db, err := bolt.Open(path, 0600, nil) 17 | if err != nil { 18 | fatal(err) 19 | return 20 | } 21 | defer db.Close() 22 | 23 | // Perform consistency check. 24 | _ = db.View(func(tx *bolt.Tx) error { 25 | var count int 26 | ch := tx.Check() 27 | loop: 28 | for { 29 | select { 30 | case err, ok := <-ch: 31 | if !ok { 32 | break loop 33 | } 34 | println(err) 35 | count++ 36 | } 37 | } 38 | 39 | // Print summary of errors. 40 | if count > 0 { 41 | fatalf("%d errors found", count) 42 | } else { 43 | println("OK") 44 | } 45 | return nil 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/buckets_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | . "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt" 8 | ) 9 | 10 | // Ensure that a list of buckets can be retrieved. 11 | func TestBuckets(t *testing.T) { 12 | SetTestMode(true) 13 | open(func(db *bolt.DB, path string) { 14 | db.Update(func(tx *bolt.Tx) error { 15 | tx.CreateBucket([]byte("woojits")) 16 | tx.CreateBucket([]byte("widgets")) 17 | tx.CreateBucket([]byte("whatchits")) 18 | return nil 19 | }) 20 | db.Close() 21 | output := run("buckets", path) 22 | equals(t, "whatchits\nwidgets\nwoojits", output) 23 | }) 24 | } 25 | 26 | // Ensure that an error is reported if the database is not found. 27 | func TestBucketsDBNotFound(t *testing.T) { 28 | SetTestMode(true) 29 | output := run("buckets", "no/such/db") 30 | equals(t, "stat no/such/db: no such file or directory", output) 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David Huie 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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/bolt_test.go: -------------------------------------------------------------------------------- 1 | package bolt_test 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("\033[31m%s:%d: "+msg+"\033[39m\n\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("\033[31m%s:%d: unexpected error: %s\033[39m\n\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("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act) 34 | tb.FailNow() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | "net" 20 | "time" 21 | ) 22 | 23 | func SetNowFunc(f func() time.Time) { 24 | nowFunc = f 25 | } 26 | 27 | type nopCloser struct{ net.Conn } 28 | 29 | func (nopCloser) Close() error { return nil } 30 | 31 | // NewConnBufio is a hook for tests. 32 | func NewConnBufio(rw bufio.ReadWriter) Conn { 33 | return &conn{br: rw.Reader, bw: rw.Writer, conn: nopCloser{}} 34 | } 35 | 36 | var ( 37 | ErrNegativeInt = errNegativeInt 38 | ) 39 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Ben Johnson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /queue/redisqueue/redisqueue.go: -------------------------------------------------------------------------------- 1 | package redisqueue 2 | 3 | import "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/redis" 4 | 5 | const ( 6 | redisListName = "httpq" 7 | ) 8 | 9 | type RedisQueue struct { 10 | pool *redis.Pool 11 | } 12 | 13 | func NewRedisQueue(pool *redis.Pool) *RedisQueue { 14 | return &RedisQueue{pool} 15 | } 16 | 17 | func (r *RedisQueue) Push(bytes []byte) error { 18 | conn := r.pool.Get() 19 | defer conn.Close() 20 | 21 | _, err := conn.Do("LPUSH", redisListName, bytes) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | return nil 27 | } 28 | 29 | func (r *RedisQueue) Pop() ([]byte, error) { 30 | conn := r.pool.Get() 31 | defer conn.Close() 32 | 33 | response, err := conn.Do("RPOP", redisListName) 34 | if err != nil { 35 | return nil, err 36 | } 37 | bytes, err := redis.Bytes(response, nil) 38 | if err != redis.ErrNil && err != nil { 39 | return nil, err 40 | } 41 | 42 | return bytes, nil 43 | } 44 | 45 | func (r *RedisQueue) Size() (uint64, error) { 46 | conn := r.pool.Get() 47 | defer conn.Close() 48 | 49 | response, err := conn.Do("LLEN", redisListName) 50 | if err != nil { 51 | return 0, err 52 | } 53 | size, err := redis.Uint64(response, nil) 54 | if err != redis.ErrNil && err != nil { 55 | return 0, err 56 | } 57 | 58 | return size, nil 59 | } 60 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/pages.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | 7 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 8 | ) 9 | 10 | // Pages prints a list of all pages in a database. 11 | func Pages(path string) { 12 | if _, err := os.Stat(path); os.IsNotExist(err) { 13 | fatal(err) 14 | return 15 | } 16 | 17 | db, err := bolt.Open(path, 0600, nil) 18 | if err != nil { 19 | fatal(err) 20 | return 21 | } 22 | defer db.Close() 23 | 24 | println("ID TYPE ITEMS OVRFLW") 25 | println("======== ========== ====== ======") 26 | 27 | db.Update(func(tx *bolt.Tx) error { 28 | var id int 29 | for { 30 | p, err := tx.Page(id) 31 | if err != nil { 32 | fatalf("page error: %d: %s", id, err) 33 | } else if p == nil { 34 | break 35 | } 36 | 37 | // Only display count and overflow if this is a non-free page. 38 | var count, overflow string 39 | if p.Type != "free" { 40 | count = strconv.Itoa(p.Count) 41 | if p.OverflowCount > 0 { 42 | overflow = strconv.Itoa(p.OverflowCount) 43 | } 44 | } 45 | 46 | // Print table row. 47 | printf("%-8d %-10s %-6s %-6s\n", p.ID, p.Type, count, overflow) 48 | 49 | // Move to the next non-overflow page. 50 | id += 1 51 | if p.Type != "free" { 52 | id += p.OverflowCount 53 | } 54 | } 55 | return nil 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package httpq 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | type Server struct { 10 | httpq *Httpq 11 | } 12 | 13 | func NewServer(httpq *Httpq) *Server { 14 | return &Server{httpq} 15 | } 16 | 17 | func (s *Server) Push(w http.ResponseWriter, r *http.Request) { 18 | if err := s.httpq.PushRequest(r); err != nil { 19 | log.Printf("Error: %v", err) 20 | w.WriteHeader(http.StatusInternalServerError) 21 | return 22 | } 23 | 24 | w.WriteHeader(http.StatusOK) 25 | } 26 | 27 | func (s *Server) Pop(w http.ResponseWriter, r *http.Request) { 28 | requestBytes, err := s.httpq.PopRequestBytes() 29 | if err != nil { 30 | log.Printf("Error: %v", err) 31 | w.WriteHeader(http.StatusInternalServerError) 32 | return 33 | } 34 | 35 | if _, err := w.Write(requestBytes); err != nil { 36 | log.Printf("Error: %v", err) 37 | w.WriteHeader(http.StatusInternalServerError) 38 | return 39 | } 40 | } 41 | 42 | func (s *Server) Size(w http.ResponseWriter, r *http.Request) { 43 | size, err := s.httpq.Size() 44 | if err != nil { 45 | log.Printf("Error: %v", err) 46 | w.WriteHeader(http.StatusInternalServerError) 47 | return 48 | } 49 | 50 | sizeMap := map[string]uint64{"size": size} 51 | encoder := json.NewEncoder(w) 52 | if err := encoder.Encode(sizeMap); err != nil { 53 | log.Printf("Error: %v", err) 54 | w.WriteHeader(http.StatusInternalServerError) 55 | return 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/Makefile: -------------------------------------------------------------------------------- 1 | TEST=. 2 | BENCH=. 3 | COVERPROFILE=/tmp/c.out 4 | BRANCH=`git rev-parse --abbrev-ref HEAD` 5 | COMMIT=`git rev-parse --short HEAD` 6 | GOLDFLAGS="-X main.branch $(BRANCH) -X main.commit $(COMMIT)" 7 | 8 | default: build 9 | 10 | bench: 11 | go test -v -test.run=NOTHINCONTAINSTHIS -test.bench=$(BENCH) 12 | 13 | # http://cloc.sourceforge.net/ 14 | cloc: 15 | @cloc --not-match-f='Makefile|_test.go' . 16 | 17 | cover: fmt 18 | go test -coverprofile=$(COVERPROFILE) -test.run=$(TEST) $(COVERFLAG) . 19 | go tool cover -html=$(COVERPROFILE) 20 | rm $(COVERPROFILE) 21 | 22 | cpuprofile: fmt 23 | @go test -c 24 | @./bolt.test -test.v -test.run=$(TEST) -test.cpuprofile cpu.prof 25 | 26 | # go get github.com/kisielk/errcheck 27 | errcheck: 28 | @echo "=== errcheck ===" 29 | @errcheck github.com/boltdb/bolt 30 | 31 | fmt: 32 | @go fmt ./... 33 | 34 | get: 35 | @go get -d ./... 36 | 37 | build: get 38 | @mkdir -p bin 39 | @go build -ldflags=$(GOLDFLAGS) -a -o bin/bolt ./cmd/bolt 40 | 41 | test: fmt 42 | @go get github.com/stretchr/testify/assert 43 | @echo "=== TESTS ===" 44 | @go test -v -cover -test.run=$(TEST) 45 | @echo "" 46 | @echo "" 47 | @echo "=== CLI ===" 48 | @go test -v -test.run=$(TEST) ./cmd/bolt 49 | @echo "" 50 | @echo "" 51 | @echo "=== RACE DETECTOR ===" 52 | @go test -v -race -test.run="TestSimulate_(100op|1000op)" 53 | 54 | .PHONY: bench cloc cover cpuprofile fmt memprofile test 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 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 LookupCommandInfo(commandName string) CommandInfo { 44 | return commandInfos[strings.ToUpper(commandName)] 45 | } 46 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/keys_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | . "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt" 8 | ) 9 | 10 | // Ensure that a list of keys can be retrieved for a given bucket. 11 | func TestKeys(t *testing.T) { 12 | SetTestMode(true) 13 | open(func(db *bolt.DB, path string) { 14 | db.Update(func(tx *bolt.Tx) error { 15 | tx.CreateBucket([]byte("widgets")) 16 | tx.Bucket([]byte("widgets")).Put([]byte("0002"), []byte("")) 17 | tx.Bucket([]byte("widgets")).Put([]byte("0001"), []byte("")) 18 | tx.Bucket([]byte("widgets")).Put([]byte("0003"), []byte("")) 19 | return nil 20 | }) 21 | db.Close() 22 | output := run("keys", path, "widgets") 23 | equals(t, "0001\n0002\n0003", output) 24 | }) 25 | } 26 | 27 | // Ensure that an error is reported if the database is not found. 28 | func TestKeysDBNotFound(t *testing.T) { 29 | SetTestMode(true) 30 | output := run("keys", "no/such/db", "widgets") 31 | equals(t, "stat no/such/db: no such file or directory", output) 32 | } 33 | 34 | // Ensure that an error is reported if the bucket is not found. 35 | func TestKeysBucketNotFound(t *testing.T) { 36 | SetTestMode(true) 37 | open(func(db *bolt.DB, path string) { 38 | db.Close() 39 | output := run("keys", path, "widgets") 40 | equals(t, "bucket not found: widgets", output) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /httpq.go: -------------------------------------------------------------------------------- 1 | package httpq 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "net/http" 8 | "net/http/httputil" 9 | ) 10 | 11 | type Httpq struct { 12 | q Queue 13 | debug bool 14 | } 15 | 16 | func NewHttpq(q Queue, debug bool) *Httpq { 17 | return &Httpq{q, debug} 18 | } 19 | 20 | func (h *Httpq) PushRequest(r *http.Request) error { 21 | requestBytes, err := httputil.DumpRequest(r, true) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | if h.debug { 27 | fmt.Printf("pushed request:\n%s", string(requestBytes)) 28 | } 29 | 30 | if err := h.q.Push(requestBytes); err != nil { 31 | return err 32 | } 33 | 34 | return nil 35 | } 36 | 37 | func (h *Httpq) PopRequestBytes() ([]byte, error) { 38 | requestBytes, err := h.q.Pop() 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | if h.debug { 44 | fmt.Printf("popped request:\n%s", string(requestBytes)) 45 | } 46 | 47 | return requestBytes, nil 48 | } 49 | 50 | func (h *Httpq) PopRequest() (*http.Request, error) { 51 | requestBytes, err := h.PopRequestBytes() 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | if requestBytes == nil { 57 | return nil, nil 58 | } 59 | 60 | buf := bytes.NewBuffer(requestBytes) 61 | requestBytesReader := bufio.NewReader(buf) 62 | request, err := http.ReadRequest(requestBytesReader) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | return request, nil 68 | } 69 | 70 | func (h *Httpq) Size() (uint64, error) { 71 | return h.q.Size() 72 | } 73 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/get_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 7 | . "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt" 8 | ) 9 | 10 | // Ensure that a value can be retrieved from the CLI. 11 | func TestGet(t *testing.T) { 12 | SetTestMode(true) 13 | open(func(db *bolt.DB, path string) { 14 | db.Update(func(tx *bolt.Tx) error { 15 | tx.CreateBucket([]byte("widgets")) 16 | tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 17 | return nil 18 | }) 19 | db.Close() 20 | output := run("get", path, "widgets", "foo") 21 | equals(t, "bar", output) 22 | }) 23 | } 24 | 25 | // Ensure that an error is reported if the database is not found. 26 | func TestGetDBNotFound(t *testing.T) { 27 | SetTestMode(true) 28 | output := run("get", "no/such/db", "widgets", "foo") 29 | equals(t, "stat no/such/db: no such file or directory", output) 30 | } 31 | 32 | // Ensure that an error is reported if the bucket is not found. 33 | func TestGetBucketNotFound(t *testing.T) { 34 | SetTestMode(true) 35 | open(func(db *bolt.DB, path string) { 36 | db.Close() 37 | output := run("get", path, "widgets", "foo") 38 | equals(t, "bucket not found: widgets", output) 39 | }) 40 | } 41 | 42 | // Ensure that an error is reported if the key is not found. 43 | func TestGetKeyNotFound(t *testing.T) { 44 | SetTestMode(true) 45 | open(func(db *bolt.DB, path string) { 46 | db.Update(func(tx *bolt.Tx) error { 47 | _, err := tx.CreateBucket([]byte("widgets")) 48 | return err 49 | }) 50 | db.Close() 51 | output := run("get", path, "widgets", "foo") 52 | equals(t, "key not found: foo", output) 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /cmd/httpq/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/DavidHuie/httpq" 9 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 10 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/redis" 11 | "github.com/DavidHuie/httpq/queue/boltqueue" 12 | "github.com/DavidHuie/httpq/queue/redisqueue" 13 | ) 14 | 15 | func main() { 16 | var dbPath string 17 | flag.StringVar(&dbPath, "db_path", "httpq.db", "the path to the database file") 18 | var port string 19 | flag.StringVar(&port, "port", ":3000", "the port to listen on") 20 | var redisUrl string 21 | flag.StringVar(&redisUrl, "redis_url", ":6379", "the url for Redis") 22 | var redisIdleConnections int 23 | flag.IntVar(&redisIdleConnections, "redis_idle_connections", 50, "maximum number of idle Redis connections") 24 | var useRedis bool 25 | flag.BoolVar(&useRedis, "redis", false, "use Redis for persistence") 26 | var debug bool 27 | flag.BoolVar(&debug, "debug", false, "enable debug mode") 28 | flag.Parse() 29 | 30 | var queue httpq.Queue 31 | 32 | if useRedis { 33 | connManager := NewRedisConnManager(redisUrl) 34 | redisPool := redis.NewPool(connManager.newRedisConn, redisIdleConnections) 35 | defer redisPool.Close() 36 | queue = redisqueue.NewRedisQueue(redisPool) 37 | } else { 38 | db, err := bolt.Open(dbPath, 0600, nil) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | defer db.Close() 43 | queue = boltqueue.NewBoltQueue(db) 44 | } 45 | 46 | hq := httpq.NewHttpq(queue, debug) 47 | server := httpq.NewServer(hq) 48 | 49 | http.HandleFunc("/push", server.Push) 50 | http.HandleFunc("/pop", server.Pop) 51 | http.HandleFunc("/size", server.Size) 52 | 53 | panic(http.ListenAndServe(port, nil)) 54 | } 55 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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/DavidHuie/httpq/Godeps/_workspace/src/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 | return nil, err 53 | } 54 | 55 | n, err := redis.Int(c.Do("DBSIZE")) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | if n != 0 { 61 | return nil, errors.New("database #9 is not empty, test can not continue") 62 | } 63 | 64 | return testConn{c}, nil 65 | } 66 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package bolt implements a low-level key/value store in pure Go. It supports 3 | fully serializable transactions, ACID semantics, and lock-free MVCC with 4 | multiple readers and a single writer. Bolt can be used for projects that 5 | want a simple data store without the need to add large dependencies such as 6 | Postgres or MySQL. 7 | 8 | Bolt is a single-level, zero-copy, B+tree data store. This means that Bolt is 9 | optimized for fast read access and does not require recovery in the event of a 10 | system crash. Transactions which have not finished committing will simply be 11 | rolled back in the event of a crash. 12 | 13 | The design of Bolt is based on Howard Chu's LMDB database project. 14 | 15 | Bolt currently works on Windows, Mac OS X, and Linux. 16 | 17 | 18 | Basics 19 | 20 | There are only a few types in Bolt: DB, Bucket, Tx, and Cursor. The DB is 21 | a collection of buckets and is represented by a single file on disk. A bucket is 22 | a collection of unique keys that are associated with values. 23 | 24 | Transactions provide either read-only or read-write access to the database. 25 | Read-only transactions can retrieve key/value pairs and can use Cursors to 26 | iterate over the dataset sequentially. Read-write transactions can create and 27 | delete buckets and can insert and remove keys. Only one read-write transaction 28 | is allowed at a time. 29 | 30 | 31 | Caveats 32 | 33 | The database uses a read-only, memory-mapped data file to ensure that 34 | applications cannot corrupt the database, however, this means that keys and 35 | values returned from Bolt cannot be changed. Writing to a read-only byte slice 36 | will cause Go to panic. 37 | 38 | Keys and values retrieved from the database are only valid for the life of 39 | the transaction. When used outside the transaction, these byte slices can 40 | point to different data or can point to invalid memory which will cause a panic. 41 | 42 | 43 | */ 44 | package bolt 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/stats_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 9 | . "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt" 10 | ) 11 | 12 | func TestStats(t *testing.T) { 13 | if os.Getpagesize() != 4096 { 14 | t.Skip() 15 | } 16 | SetTestMode(true) 17 | open(func(db *bolt.DB, path string) { 18 | db.Update(func(tx *bolt.Tx) error { 19 | b, err := tx.CreateBucket([]byte("foo")) 20 | if err != nil { 21 | return err 22 | } 23 | for i := 0; i < 10; i++ { 24 | b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))) 25 | } 26 | b, err = tx.CreateBucket([]byte("bar")) 27 | if err != nil { 28 | return err 29 | } 30 | for i := 0; i < 100; i++ { 31 | b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))) 32 | } 33 | b, err = tx.CreateBucket([]byte("baz")) 34 | if err != nil { 35 | return err 36 | } 37 | b.Put([]byte("key"), []byte("value")) 38 | return nil 39 | }) 40 | db.Close() 41 | output := run("stats", path, "b") 42 | equals(t, "Aggregate statistics for 2 buckets\n\n"+ 43 | "Page count statistics\n"+ 44 | "\tNumber of logical branch pages: 0\n"+ 45 | "\tNumber of physical branch overflow pages: 0\n"+ 46 | "\tNumber of logical leaf pages: 1\n"+ 47 | "\tNumber of physical leaf overflow pages: 0\n"+ 48 | "Tree statistics\n"+ 49 | "\tNumber of keys/value pairs: 101\n"+ 50 | "\tNumber of levels in B+tree: 1\n"+ 51 | "Page size utilization\n"+ 52 | "\tBytes allocated for physical branch pages: 0\n"+ 53 | "\tBytes actually used for branch data: 0 (0%)\n"+ 54 | "\tBytes allocated for physical leaf pages: 4096\n"+ 55 | "\tBytes actually used for leaf data: 1996 (48%)\n"+ 56 | "Bucket statistics\n"+ 57 | "\tTotal number of buckets: 2\n"+ 58 | "\tTotal number on inlined buckets: 1 (50%)\n"+ 59 | "\tBytes used for inlined buckets: 40 (2%)", output) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/bolt_windows.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "syscall" 7 | "time" 8 | "unsafe" 9 | ) 10 | 11 | var odirect int 12 | 13 | // fdatasync flushes written data to a file descriptor. 14 | func fdatasync(db *DB) error { 15 | return db.file.Sync() 16 | } 17 | 18 | // flock acquires an advisory lock on a file descriptor. 19 | func flock(f *os.File, _ time.Duration) error { 20 | return nil 21 | } 22 | 23 | // funlock releases an advisory lock on a file descriptor. 24 | func funlock(f *os.File) error { 25 | return nil 26 | } 27 | 28 | // mmap memory maps a DB's data file. 29 | // Based on: https://github.com/edsrzf/mmap-go 30 | func mmap(db *DB, sz int) error { 31 | // Truncate the database to the size of the mmap. 32 | if err := db.file.Truncate(int64(sz)); err != nil { 33 | return fmt.Errorf("truncate: %s", err) 34 | } 35 | 36 | // Open a file mapping handle. 37 | sizelo := uint32(sz >> 32) 38 | sizehi := uint32(sz) & 0xffffffff 39 | h, errno := syscall.CreateFileMapping(syscall.Handle(db.file.Fd()), nil, syscall.PAGE_READONLY, sizelo, sizehi, nil) 40 | if h == 0 { 41 | return os.NewSyscallError("CreateFileMapping", errno) 42 | } 43 | 44 | // Create the memory map. 45 | addr, errno := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, uintptr(sz)) 46 | if addr == 0 { 47 | return os.NewSyscallError("MapViewOfFile", errno) 48 | } 49 | 50 | // Close mapping handle. 51 | if err := syscall.CloseHandle(syscall.Handle(h)); err != nil { 52 | return os.NewSyscallError("CloseHandle", err) 53 | } 54 | 55 | // Convert to a byte array. 56 | db.data = ((*[maxMapSize]byte)(unsafe.Pointer(addr))) 57 | db.datasz = sz 58 | 59 | return nil 60 | } 61 | 62 | // munmap unmaps a pointer from a file. 63 | // Based on: https://github.com/edsrzf/mmap-go 64 | func munmap(db *DB) error { 65 | if db.data == nil { 66 | return nil 67 | } 68 | 69 | addr := (uintptr)(unsafe.Pointer(&db.data[0])) 70 | if err := syscall.UnmapViewOfFile(addr); err != nil { 71 | return os.NewSyscallError("UnmapViewOfFile", err) 72 | } 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/main_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | "reflect" 9 | "runtime" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 14 | . "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt" 15 | ) 16 | 17 | // open creates and opens a Bolt database in the temp directory. 18 | func open(fn func(*bolt.DB, string)) { 19 | path := tempfile() 20 | defer os.RemoveAll(path) 21 | 22 | db, err := bolt.Open(path, 0600, nil) 23 | if err != nil { 24 | panic("db open error: " + err.Error()) 25 | } 26 | fn(db, path) 27 | } 28 | 29 | // run executes a command against the CLI and returns the output. 30 | func run(args ...string) string { 31 | args = append([]string{"bolt"}, args...) 32 | NewApp().Run(args) 33 | return strings.TrimSpace(LogBuffer()) 34 | } 35 | 36 | // tempfile returns a temporary file path. 37 | func tempfile() string { 38 | f, _ := ioutil.TempFile("", "bolt-") 39 | f.Close() 40 | os.Remove(f.Name()) 41 | return f.Name() 42 | } 43 | 44 | // assert fails the test if the condition is false. 45 | func assert(tb testing.TB, condition bool, msg string, v ...interface{}) { 46 | if !condition { 47 | _, file, line, _ := runtime.Caller(1) 48 | fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...) 49 | tb.FailNow() 50 | } 51 | } 52 | 53 | // ok fails the test if an err is not nil. 54 | func ok(tb testing.TB, err error) { 55 | if err != nil { 56 | _, file, line, _ := runtime.Caller(1) 57 | fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error()) 58 | tb.FailNow() 59 | } 60 | } 61 | 62 | // equals fails the test if exp is not equal to act. 63 | func equals(tb testing.TB, exp, act interface{}) { 64 | if !reflect.DeepEqual(exp, act) { 65 | _, file, line, _ := runtime.Caller(1) 66 | fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act) 67 | tb.FailNow() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/bolt_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!plan9 2 | 3 | package bolt 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "syscall" 9 | "time" 10 | "unsafe" 11 | ) 12 | 13 | // flock acquires an advisory lock on a file descriptor. 14 | func flock(f *os.File, timeout time.Duration) error { 15 | var t time.Time 16 | for { 17 | // If we're beyond our timeout then return an error. 18 | // This can only occur after we've attempted a flock once. 19 | if t.IsZero() { 20 | t = time.Now() 21 | } else if timeout > 0 && time.Since(t) > timeout { 22 | return ErrTimeout 23 | } 24 | 25 | // Otherwise attempt to obtain an exclusive lock. 26 | err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB) 27 | if err == nil { 28 | return nil 29 | } else if err != syscall.EWOULDBLOCK { 30 | return err 31 | } 32 | 33 | // Wait for a bit and try again. 34 | time.Sleep(50 * time.Millisecond) 35 | } 36 | } 37 | 38 | // funlock releases an advisory lock on a file descriptor. 39 | func funlock(f *os.File) error { 40 | return syscall.Flock(int(f.Fd()), syscall.LOCK_UN) 41 | } 42 | 43 | // mmap memory maps a DB's data file. 44 | func mmap(db *DB, sz int) error { 45 | // Truncate and fsync to ensure file size metadata is flushed. 46 | // https://github.com/boltdb/bolt/issues/284 47 | if err := db.file.Truncate(int64(sz)); err != nil { 48 | return fmt.Errorf("file resize error: %s", err) 49 | } 50 | if err := db.file.Sync(); err != nil { 51 | return fmt.Errorf("file sync error: %s", err) 52 | } 53 | 54 | // Map the data file to memory. 55 | b, err := syscall.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ, syscall.MAP_SHARED) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | // Save the original byte slice and convert to a byte array pointer. 61 | db.dataref = b 62 | db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0])) 63 | db.datasz = sz 64 | return nil 65 | } 66 | 67 | // munmap unmaps a DB's data file from memory. 68 | func munmap(db *DB) error { 69 | // Ignore the unmap if we have no mapped data. 70 | if db.dataref == nil { 71 | return nil 72 | } 73 | 74 | // Unmap using the original byte slice. 75 | err := syscall.Munmap(db.dataref) 76 | db.dataref = nil 77 | db.data = nil 78 | db.datasz = 0 79 | return err 80 | } 81 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/quick_test.go: -------------------------------------------------------------------------------- 1 | package bolt_test 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "math/rand" 8 | "os" 9 | "reflect" 10 | "testing/quick" 11 | "time" 12 | ) 13 | 14 | // testing/quick defaults to 5 iterations and a random seed. 15 | // You can override these settings from the command line: 16 | // 17 | // -quick.count The number of iterations to perform. 18 | // -quick.seed The seed to use for randomizing. 19 | // -quick.maxitems The maximum number of items to insert into a DB. 20 | // -quick.maxksize The maximum size of a key. 21 | // -quick.maxvsize The maximum size of a value. 22 | // 23 | 24 | var qcount, qseed, qmaxitems, qmaxksize, qmaxvsize int 25 | 26 | func init() { 27 | flag.IntVar(&qcount, "quick.count", 5, "") 28 | flag.IntVar(&qseed, "quick.seed", int(time.Now().UnixNano())%100000, "") 29 | flag.IntVar(&qmaxitems, "quick.maxitems", 1000, "") 30 | flag.IntVar(&qmaxksize, "quick.maxksize", 1024, "") 31 | flag.IntVar(&qmaxvsize, "quick.maxvsize", 1024, "") 32 | flag.Parse() 33 | fmt.Fprintln(os.Stderr, "seed:", qseed) 34 | fmt.Fprintf(os.Stderr, "quick settings: count=%v, items=%v, ksize=%v, vsize=%v\n", qcount, qmaxitems, qmaxksize, qmaxvsize) 35 | } 36 | 37 | func qconfig() *quick.Config { 38 | return &quick.Config{ 39 | MaxCount: qcount, 40 | Rand: rand.New(rand.NewSource(int64(qseed))), 41 | } 42 | } 43 | 44 | type testdata []testdataitem 45 | 46 | func (t testdata) Len() int { return len(t) } 47 | func (t testdata) Swap(i, j int) { t[i], t[j] = t[j], t[i] } 48 | func (t testdata) Less(i, j int) bool { return bytes.Compare(t[i].Key, t[j].Key) == -1 } 49 | 50 | func (t testdata) Generate(rand *rand.Rand, size int) reflect.Value { 51 | n := rand.Intn(qmaxitems-1) + 1 52 | items := make(testdata, n) 53 | for i := 0; i < n; i++ { 54 | item := &items[i] 55 | item.Key = randByteSlice(rand, 1, qmaxksize) 56 | item.Value = randByteSlice(rand, 0, qmaxvsize) 57 | } 58 | return reflect.ValueOf(items) 59 | } 60 | 61 | type revtestdata []testdataitem 62 | 63 | func (t revtestdata) Len() int { return len(t) } 64 | func (t revtestdata) Swap(i, j int) { t[i], t[j] = t[j], t[i] } 65 | func (t revtestdata) Less(i, j int) bool { return bytes.Compare(t[i].Key, t[j].Key) == 1 } 66 | 67 | type testdataitem struct { 68 | Key []byte 69 | Value []byte 70 | } 71 | 72 | func randByteSlice(rand *rand.Rand, minSize, maxSize int) []byte { 73 | n := rand.Intn(maxSize-minSize) + minSize 74 | b := make([]byte, n) 75 | for i := 0; i < n; i++ { 76 | b[i] = byte(rand.Intn(255)) 77 | } 78 | return b 79 | } 80 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/stats.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | 7 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 8 | ) 9 | 10 | // Collect stats for all top level buckets matching the prefix. 11 | func Stats(path, prefix string) { 12 | if _, err := os.Stat(path); os.IsNotExist(err) { 13 | fatal(err) 14 | return 15 | } 16 | 17 | db, err := bolt.Open(path, 0600, nil) 18 | if err != nil { 19 | fatal(err) 20 | return 21 | } 22 | defer db.Close() 23 | 24 | err = db.View(func(tx *bolt.Tx) error { 25 | var s bolt.BucketStats 26 | var count int 27 | var prefix = []byte(prefix) 28 | tx.ForEach(func(name []byte, b *bolt.Bucket) error { 29 | if bytes.HasPrefix(name, prefix) { 30 | s.Add(b.Stats()) 31 | count += 1 32 | } 33 | return nil 34 | }) 35 | printf("Aggregate statistics for %d buckets\n\n", count) 36 | 37 | println("Page count statistics") 38 | printf("\tNumber of logical branch pages: %d\n", s.BranchPageN) 39 | printf("\tNumber of physical branch overflow pages: %d\n", s.BranchOverflowN) 40 | printf("\tNumber of logical leaf pages: %d\n", s.LeafPageN) 41 | printf("\tNumber of physical leaf overflow pages: %d\n", s.LeafOverflowN) 42 | 43 | println("Tree statistics") 44 | printf("\tNumber of keys/value pairs: %d\n", s.KeyN) 45 | printf("\tNumber of levels in B+tree: %d\n", s.Depth) 46 | 47 | println("Page size utilization") 48 | printf("\tBytes allocated for physical branch pages: %d\n", s.BranchAlloc) 49 | var percentage int 50 | if s.BranchAlloc != 0 { 51 | percentage = int(float32(s.BranchInuse) * 100.0 / float32(s.BranchAlloc)) 52 | } 53 | printf("\tBytes actually used for branch data: %d (%d%%)\n", s.BranchInuse, percentage) 54 | printf("\tBytes allocated for physical leaf pages: %d\n", s.LeafAlloc) 55 | percentage = 0 56 | if s.LeafAlloc != 0 { 57 | percentage = int(float32(s.LeafInuse) * 100.0 / float32(s.LeafAlloc)) 58 | } 59 | printf("\tBytes actually used for leaf data: %d (%d%%)\n", s.LeafInuse, percentage) 60 | 61 | println("Bucket statistics") 62 | printf("\tTotal number of buckets: %d\n", s.BucketN) 63 | percentage = int(float32(s.InlineBucketN) * 100.0 / float32(s.BucketN)) 64 | printf("\tTotal number on inlined buckets: %d (%d%%)\n", s.InlineBucketN, percentage) 65 | percentage = 0 66 | if s.LeafInuse != 0 { 67 | percentage = int(float32(s.InlineBucketInuse) * 100.0 / float32(s.LeafInuse)) 68 | } 69 | printf("\tBytes used for inlined buckets: %d (%d%%)\n", s.InlineBucketInuse, percentage) 70 | 71 | return nil 72 | }) 73 | if err != nil { 74 | fatal(err) 75 | return 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/errors.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import "errors" 4 | 5 | // These errors can be returned when opening or calling methods on a DB. 6 | var ( 7 | // ErrDatabaseNotOpen is returned when a DB instance is accessed before it 8 | // is opened or after it is closed. 9 | ErrDatabaseNotOpen = errors.New("database not open") 10 | 11 | // ErrDatabaseOpen is returned when opening a database that is 12 | // already open. 13 | ErrDatabaseOpen = errors.New("database already open") 14 | 15 | // ErrInvalid is returned when a data file is not a Bolt-formatted database. 16 | ErrInvalid = errors.New("invalid database") 17 | 18 | // ErrVersionMismatch is returned when the data file was created with a 19 | // different version of Bolt. 20 | ErrVersionMismatch = errors.New("version mismatch") 21 | 22 | // ErrChecksum is returned when either meta page checksum does not match. 23 | ErrChecksum = errors.New("checksum error") 24 | 25 | // ErrTimeout is returned when a database cannot obtain an exclusive lock 26 | // on the data file after the timeout passed to Open(). 27 | ErrTimeout = errors.New("timeout") 28 | ) 29 | 30 | // These errors can occur when beginning or committing a Tx. 31 | var ( 32 | // ErrTxNotWritable is returned when performing a write operation on a 33 | // read-only transaction. 34 | ErrTxNotWritable = errors.New("tx not writable") 35 | 36 | // ErrTxClosed is returned when committing or rolling back a transaction 37 | // that has already been committed or rolled back. 38 | ErrTxClosed = errors.New("tx closed") 39 | ) 40 | 41 | // These errors can occur when putting or deleting a value or a bucket. 42 | var ( 43 | // ErrBucketNotFound is returned when trying to access a bucket that has 44 | // not been created yet. 45 | ErrBucketNotFound = errors.New("bucket not found") 46 | 47 | // ErrBucketExists is returned when creating a bucket that already exists. 48 | ErrBucketExists = errors.New("bucket already exists") 49 | 50 | // ErrBucketNameRequired is returned when creating a bucket with a blank name. 51 | ErrBucketNameRequired = errors.New("bucket name required") 52 | 53 | // ErrKeyRequired is returned when inserting a zero-length key. 54 | ErrKeyRequired = errors.New("key required") 55 | 56 | // ErrKeyTooLarge is returned when inserting a key that is larger than MaxKeySize. 57 | ErrKeyTooLarge = errors.New("key too large") 58 | 59 | // ErrValueTooLarge is returned when inserting a value that is larger than MaxValueSize. 60 | ErrValueTooLarge = errors.New("value too large") 61 | 62 | // ErrIncompatibleValue is returned when trying create or delete a bucket 63 | // on an existing non-bucket key or when trying to create or delete a 64 | // non-bucket key on an existing bucket key. 65 | ErrIncompatibleValue = errors.New("incompatible value") 66 | ) 67 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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/DavidHuie/httpq/Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/internal/redistest" 24 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/redis" 25 | ) 26 | 27 | func ExampleScript(c redis.Conn, reply interface{}, err error) { 28 | // Initialize a package-level variable with a script. 29 | var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`) 30 | 31 | // In a function, use the script Do method to evaluate the script. The Do 32 | // method optimistically uses the EVALSHA command. If the script is not 33 | // loaded, then the Do method falls back to the EVAL command. 34 | reply, err = getScript.Do(c, "foo") 35 | } 36 | 37 | func TestScript(t *testing.T) { 38 | c, err := redistest.Dial() 39 | if err != nil { 40 | t.Fatalf("error connection to database, %v", err) 41 | } 42 | defer c.Close() 43 | 44 | // To test fall back in Do, we make script unique by adding comment with current time. 45 | script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano()) 46 | s := redis.NewScript(2, script) 47 | reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")} 48 | 49 | v, err := s.Do(c, "key1", "key2", "arg1", "arg2") 50 | if err != nil { 51 | t.Errorf("s.Do(c, ...) returned %v", err) 52 | } 53 | 54 | if !reflect.DeepEqual(v, reply) { 55 | t.Errorf("s.Do(c, ..); = %v, want %v", v, reply) 56 | } 57 | 58 | err = s.Load(c) 59 | if err != nil { 60 | t.Errorf("s.Load(c) returned %v", err) 61 | } 62 | 63 | err = s.SendHash(c, "key1", "key2", "arg1", "arg2") 64 | if err != nil { 65 | t.Errorf("s.SendHash(c, ...) returned %v", err) 66 | } 67 | 68 | err = c.Flush() 69 | if err != nil { 70 | t.Errorf("c.Flush() returned %v", err) 71 | } 72 | 73 | v, err = c.Receive() 74 | if !reflect.DeepEqual(v, reply) { 75 | t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply) 76 | } 77 | 78 | err = s.Send(c, "key1", "key2", "arg1", "arg2") 79 | if err != nil { 80 | t.Errorf("s.Send(c, ...) returned %v", err) 81 | } 82 | 83 | err = c.Flush() 84 | if err != nil { 85 | t.Errorf("c.Flush() returned %v", err) 86 | } 87 | 88 | v, err = c.Receive() 89 | if !reflect.DeepEqual(v, reply) { 90 | t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply) 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 ( 18 | "errors" 19 | ) 20 | 21 | // Subscription represents a subscribe or unsubscribe notification. 22 | type Subscription struct { 23 | 24 | // Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe" 25 | Kind string 26 | 27 | // The channel that was changed. 28 | Channel string 29 | 30 | // The current number of subscriptions for connection. 31 | Count int 32 | } 33 | 34 | // Message represents a message notification. 35 | type Message struct { 36 | 37 | // The originating channel. 38 | Channel string 39 | 40 | // The message data. 41 | Data []byte 42 | } 43 | 44 | // PMessage represents a pmessage notification. 45 | type PMessage struct { 46 | 47 | // The matched pattern. 48 | Pattern string 49 | 50 | // The originating channel. 51 | Channel string 52 | 53 | // The message data. 54 | Data []byte 55 | } 56 | 57 | // PubSubConn wraps a Conn with convenience methods for subscribers. 58 | type PubSubConn struct { 59 | Conn Conn 60 | } 61 | 62 | // Close closes the connection. 63 | func (c PubSubConn) Close() error { 64 | return c.Conn.Close() 65 | } 66 | 67 | // Subscribe subscribes the connection to the specified channels. 68 | func (c PubSubConn) Subscribe(channel ...interface{}) error { 69 | c.Conn.Send("SUBSCRIBE", channel...) 70 | return c.Conn.Flush() 71 | } 72 | 73 | // PSubscribe subscribes the connection to the given patterns. 74 | func (c PubSubConn) PSubscribe(channel ...interface{}) error { 75 | c.Conn.Send("PSUBSCRIBE", channel...) 76 | return c.Conn.Flush() 77 | } 78 | 79 | // Unsubscribe unsubscribes the connection from the given channels, or from all 80 | // of them if none is given. 81 | func (c PubSubConn) Unsubscribe(channel ...interface{}) error { 82 | c.Conn.Send("UNSUBSCRIBE", channel...) 83 | return c.Conn.Flush() 84 | } 85 | 86 | // PUnsubscribe unsubscribes the connection from the given patterns, or from all 87 | // of them if none is given. 88 | func (c PubSubConn) PUnsubscribe(channel ...interface{}) error { 89 | c.Conn.Send("PUNSUBSCRIBE", channel...) 90 | return c.Conn.Flush() 91 | } 92 | 93 | // Receive returns a pushed message as a Subscription, Message, PMessage or 94 | // error. The return value is intended to be used directly in a type switch as 95 | // illustrated in the PubSubConn example. 96 | func (c PubSubConn) Receive() interface{} { 97 | reply, err := Values(c.Conn.Receive()) 98 | if err != nil { 99 | return err 100 | } 101 | 102 | var kind string 103 | reply, err = Scan(reply, &kind) 104 | if err != nil { 105 | return err 106 | } 107 | 108 | switch kind { 109 | case "message": 110 | var m Message 111 | if _, err := Scan(reply, &m.Channel, &m.Data); err != nil { 112 | return err 113 | } 114 | return m 115 | case "pmessage": 116 | var pm PMessage 117 | if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil { 118 | return err 119 | } 120 | return pm 121 | case "subscribe", "psubscribe", "unsubscribe", "punsubscribe": 122 | s := Subscription{Kind: kind} 123 | if _, err := Scan(reply, &s.Channel, &s.Count); err != nil { 124 | return err 125 | } 126 | return s 127 | } 128 | return errors.New("redigo: unknown pubsub notification") 129 | } 130 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/page.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "unsafe" 7 | ) 8 | 9 | const pageHeaderSize = int(unsafe.Offsetof(((*page)(nil)).ptr)) 10 | 11 | const minKeysPerPage = 2 12 | 13 | const branchPageElementSize = int(unsafe.Sizeof(branchPageElement{})) 14 | const leafPageElementSize = int(unsafe.Sizeof(leafPageElement{})) 15 | 16 | const ( 17 | branchPageFlag = 0x01 18 | leafPageFlag = 0x02 19 | metaPageFlag = 0x04 20 | freelistPageFlag = 0x10 21 | ) 22 | 23 | const ( 24 | bucketLeafFlag = 0x01 25 | ) 26 | 27 | type pgid uint64 28 | 29 | type page struct { 30 | id pgid 31 | flags uint16 32 | count uint16 33 | overflow uint32 34 | ptr uintptr 35 | } 36 | 37 | // typ returns a human readable page type string used for debugging. 38 | func (p *page) typ() string { 39 | if (p.flags & branchPageFlag) != 0 { 40 | return "branch" 41 | } else if (p.flags & leafPageFlag) != 0 { 42 | return "leaf" 43 | } else if (p.flags & metaPageFlag) != 0 { 44 | return "meta" 45 | } else if (p.flags & freelistPageFlag) != 0 { 46 | return "freelist" 47 | } 48 | return fmt.Sprintf("unknown<%02x>", p.flags) 49 | } 50 | 51 | // meta returns a pointer to the metadata section of the page. 52 | func (p *page) meta() *meta { 53 | return (*meta)(unsafe.Pointer(&p.ptr)) 54 | } 55 | 56 | // leafPageElement retrieves the leaf node by index 57 | func (p *page) leafPageElement(index uint16) *leafPageElement { 58 | n := &((*[0x7FFFFFF]leafPageElement)(unsafe.Pointer(&p.ptr)))[index] 59 | return n 60 | } 61 | 62 | // leafPageElements retrieves a list of leaf nodes. 63 | func (p *page) leafPageElements() []leafPageElement { 64 | return ((*[0x7FFFFFF]leafPageElement)(unsafe.Pointer(&p.ptr)))[:] 65 | } 66 | 67 | // branchPageElement retrieves the branch node by index 68 | func (p *page) branchPageElement(index uint16) *branchPageElement { 69 | return &((*[0x7FFFFFF]branchPageElement)(unsafe.Pointer(&p.ptr)))[index] 70 | } 71 | 72 | // branchPageElements retrieves a list of branch nodes. 73 | func (p *page) branchPageElements() []branchPageElement { 74 | return ((*[0x7FFFFFF]branchPageElement)(unsafe.Pointer(&p.ptr)))[:] 75 | } 76 | 77 | // dump writes n bytes of the page to STDERR as hex output. 78 | func (p *page) hexdump(n int) { 79 | buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:n] 80 | fmt.Fprintf(os.Stderr, "%x\n", buf) 81 | } 82 | 83 | type pages []*page 84 | 85 | func (s pages) Len() int { return len(s) } 86 | func (s pages) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 87 | func (s pages) Less(i, j int) bool { return s[i].id < s[j].id } 88 | 89 | // branchPageElement represents a node on a branch page. 90 | type branchPageElement struct { 91 | pos uint32 92 | ksize uint32 93 | pgid pgid 94 | } 95 | 96 | // key returns a byte slice of the node key. 97 | func (n *branchPageElement) key() []byte { 98 | buf := (*[maxAllocSize]byte)(unsafe.Pointer(n)) 99 | return buf[n.pos : n.pos+n.ksize] 100 | } 101 | 102 | // leafPageElement represents a node on a leaf page. 103 | type leafPageElement struct { 104 | flags uint32 105 | pos uint32 106 | ksize uint32 107 | vsize uint32 108 | } 109 | 110 | // key returns a byte slice of the node key. 111 | func (n *leafPageElement) key() []byte { 112 | buf := (*[maxAllocSize]byte)(unsafe.Pointer(n)) 113 | return buf[n.pos : n.pos+n.ksize] 114 | } 115 | 116 | // value returns a byte slice of the node value. 117 | func (n *leafPageElement) value() []byte { 118 | buf := (*[maxAllocSize]byte)(unsafe.Pointer(n)) 119 | return buf[n.pos+n.ksize : n.pos+n.ksize+n.vsize] 120 | } 121 | 122 | // PageInfo represents human readable information about a page. 123 | type PageInfo struct { 124 | ID int 125 | Type string 126 | Count int 127 | OverflowCount int 128 | } 129 | 130 | type pgids []pgid 131 | 132 | func (s pgids) Len() int { return len(s) } 133 | func (s pgids) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 134 | func (s pgids) Less(i, j int) bool { return s[i] < s[j] } 135 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/freelist_test.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "unsafe" 7 | ) 8 | 9 | // Ensure that a page is added to a transaction's freelist. 10 | func TestFreelist_free(t *testing.T) { 11 | f := newFreelist() 12 | f.free(100, &page{id: 12}) 13 | if !reflect.DeepEqual([]pgid{12}, f.pending[100]) { 14 | t.Fatalf("exp=%v; got=%v", []pgid{12}, f.pending[100]) 15 | } 16 | } 17 | 18 | // Ensure that a page and its overflow is added to a transaction's freelist. 19 | func TestFreelist_free_overflow(t *testing.T) { 20 | f := newFreelist() 21 | f.free(100, &page{id: 12, overflow: 3}) 22 | if exp := []pgid{12, 13, 14, 15}; !reflect.DeepEqual(exp, f.pending[100]) { 23 | t.Fatalf("exp=%v; got=%v", exp, f.pending[100]) 24 | } 25 | } 26 | 27 | // Ensure that a transaction's free pages can be released. 28 | func TestFreelist_release(t *testing.T) { 29 | f := newFreelist() 30 | f.free(100, &page{id: 12, overflow: 1}) 31 | f.free(100, &page{id: 9}) 32 | f.free(102, &page{id: 39}) 33 | f.release(100) 34 | f.release(101) 35 | if exp := []pgid{9, 12, 13}; !reflect.DeepEqual(exp, f.ids) { 36 | t.Fatalf("exp=%v; got=%v", exp, f.ids) 37 | } 38 | 39 | f.release(102) 40 | if exp := []pgid{9, 12, 13, 39}; !reflect.DeepEqual(exp, f.ids) { 41 | t.Fatalf("exp=%v; got=%v", exp, f.ids) 42 | } 43 | } 44 | 45 | // Ensure that a freelist can find contiguous blocks of pages. 46 | func TestFreelist_allocate(t *testing.T) { 47 | f := &freelist{ids: []pgid{3, 4, 5, 6, 7, 9, 12, 13, 18}} 48 | if id := int(f.allocate(3)); id != 3 { 49 | t.Fatalf("exp=3; got=%v", id) 50 | } 51 | if id := int(f.allocate(1)); id != 6 { 52 | t.Fatalf("exp=6; got=%v", id) 53 | } 54 | if id := int(f.allocate(3)); id != 0 { 55 | t.Fatalf("exp=0; got=%v", id) 56 | } 57 | if id := int(f.allocate(2)); id != 12 { 58 | t.Fatalf("exp=12; got=%v", id) 59 | } 60 | if id := int(f.allocate(1)); id != 7 { 61 | t.Fatalf("exp=7; got=%v", id) 62 | } 63 | if id := int(f.allocate(0)); id != 0 { 64 | t.Fatalf("exp=0; got=%v", id) 65 | } 66 | if id := int(f.allocate(0)); id != 0 { 67 | t.Fatalf("exp=0; got=%v", id) 68 | } 69 | if exp := []pgid{9, 18}; !reflect.DeepEqual(exp, f.ids) { 70 | t.Fatalf("exp=%v; got=%v", exp, f.ids) 71 | } 72 | 73 | if id := int(f.allocate(1)); id != 9 { 74 | t.Fatalf("exp=9; got=%v", id) 75 | } 76 | if id := int(f.allocate(1)); id != 18 { 77 | t.Fatalf("exp=18; got=%v", id) 78 | } 79 | if id := int(f.allocate(1)); id != 0 { 80 | t.Fatalf("exp=0; got=%v", id) 81 | } 82 | if exp := []pgid{}; !reflect.DeepEqual(exp, f.ids) { 83 | t.Fatalf("exp=%v; got=%v", exp, f.ids) 84 | } 85 | } 86 | 87 | // Ensure that a freelist can deserialize from a freelist page. 88 | func TestFreelist_read(t *testing.T) { 89 | // Create a page. 90 | var buf [4096]byte 91 | page := (*page)(unsafe.Pointer(&buf[0])) 92 | page.flags = freelistPageFlag 93 | page.count = 2 94 | 95 | // Insert 2 page ids. 96 | ids := (*[3]pgid)(unsafe.Pointer(&page.ptr)) 97 | ids[0] = 23 98 | ids[1] = 50 99 | 100 | // Deserialize page into a freelist. 101 | f := newFreelist() 102 | f.read(page) 103 | 104 | // Ensure that there are two page ids in the freelist. 105 | if exp := []pgid{23, 50}; !reflect.DeepEqual(exp, f.ids) { 106 | t.Fatalf("exp=%v; got=%v", exp, f.ids) 107 | } 108 | } 109 | 110 | // Ensure that a freelist can serialize into a freelist page. 111 | func TestFreelist_write(t *testing.T) { 112 | // Create a freelist and write it to a page. 113 | var buf [4096]byte 114 | f := &freelist{ids: []pgid{12, 39}, pending: make(map[txid][]pgid)} 115 | f.pending[100] = []pgid{28, 11} 116 | f.pending[101] = []pgid{3} 117 | p := (*page)(unsafe.Pointer(&buf[0])) 118 | f.write(p) 119 | 120 | // Read the page back out. 121 | f2 := newFreelist() 122 | f2.read(p) 123 | 124 | // Ensure that the freelist is correct. 125 | // All pages should be present and in reverse order. 126 | if exp := []pgid{3, 11, 12, 28, 39}; !reflect.DeepEqual(exp, f2.ids) { 127 | t.Fatalf("exp=%v; got=%v", exp, f2.ids) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /queue/boltqueue/boltqueue.go: -------------------------------------------------------------------------------- 1 | package boltqueue 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/json" 6 | 7 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 8 | ) 9 | 10 | type BoltQueue struct { 11 | conn *bolt.DB 12 | } 13 | 14 | func NewBoltQueue(conn *bolt.DB) *BoltQueue { 15 | return &BoltQueue{conn} 16 | } 17 | 18 | type queueMetadata struct { 19 | Head uint64 20 | Last uint64 21 | } 22 | 23 | func (q *queueMetadata) empty() bool { 24 | return q.Head > q.Last || (q.Head == 0 && q.Last == 0) 25 | } 26 | 27 | var ( 28 | metadataBucketName = []byte("b") 29 | metadataKey = []byte("m") 30 | dataBucketName = []byte("d") 31 | ) 32 | 33 | func getMetadata(b *bolt.Bucket) (*queueMetadata, error) { 34 | var metadata queueMetadata 35 | value := b.Get(metadataKey) 36 | 37 | // Create metadata if it doesn't exist 38 | if value == nil { 39 | metadata = queueMetadata{} 40 | bytes, err := json.Marshal(metadata) 41 | if err != nil { 42 | return nil, err 43 | } 44 | if err := b.Put(metadataKey, bytes); err != nil { 45 | return nil, err 46 | } 47 | } else { 48 | if err := json.Unmarshal(value, &metadata); err != nil { 49 | return nil, err 50 | } 51 | } 52 | 53 | return &metadata, nil 54 | } 55 | 56 | func (b *BoltQueue) Push(bytes []byte) error { 57 | return b.conn.Update(func(tx *bolt.Tx) error { 58 | mbucket, err := tx.CreateBucketIfNotExists(metadataBucketName) 59 | if err != nil { 60 | return err 61 | } 62 | dbucket, err := tx.CreateBucketIfNotExists(dataBucketName) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | metadata, err := getMetadata(mbucket) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | // Update metadata to reflect new data location 73 | metadata.Last += 1 74 | 75 | // Update recently initialized metadatas 76 | if metadata.Head == 0 { 77 | metadata.Head = 1 78 | } 79 | 80 | metadataBytes, err := json.Marshal(metadata) 81 | if err != nil { 82 | return err 83 | } 84 | if err := mbucket.Put(metadataKey, metadataBytes); err != nil { 85 | return err 86 | } 87 | 88 | // Push 89 | dataLocationBytes := make([]byte, 8) 90 | binary.PutUvarint(dataLocationBytes, metadata.Last) 91 | if err := dbucket.Put(dataLocationBytes, bytes); err != nil { 92 | return err 93 | } 94 | 95 | return nil 96 | }) 97 | } 98 | 99 | func (b *BoltQueue) Pop() ([]byte, error) { 100 | var response []byte 101 | err := b.conn.Update(func(tx *bolt.Tx) error { 102 | mbucket, err := tx.CreateBucketIfNotExists(metadataBucketName) 103 | if err != nil { 104 | return err 105 | } 106 | dbucket, err := tx.CreateBucketIfNotExists(dataBucketName) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | metadata, err := getMetadata(mbucket) 112 | if err != nil { 113 | return err 114 | } 115 | 116 | if metadata.empty() { 117 | response = nil 118 | return nil 119 | } 120 | 121 | // Perform pop 122 | dataLocationBytes := make([]byte, 8) 123 | binary.PutUvarint(dataLocationBytes, metadata.Head) 124 | response = dbucket.Get(dataLocationBytes) 125 | if response == nil { 126 | return nil 127 | } 128 | if err := dbucket.Delete(dataLocationBytes); err != nil { 129 | return err 130 | } 131 | 132 | // Update metadata 133 | metadata.Head = metadata.Head + 1 134 | metadataBytes, err := json.Marshal(metadata) 135 | if err != nil { 136 | return err 137 | } 138 | if err := mbucket.Put(metadataKey, metadataBytes); err != nil { 139 | return err 140 | } 141 | 142 | return nil 143 | }) 144 | if err != nil { 145 | return nil, err 146 | } 147 | 148 | return response, nil 149 | } 150 | 151 | func (b *BoltQueue) Size() (uint64, error) { 152 | var size uint64 153 | err := b.conn.Update(func(tx *bolt.Tx) error { 154 | mbucket, err := tx.CreateBucketIfNotExists(metadataBucketName) 155 | if err != nil { 156 | return err 157 | } 158 | metadata, err := getMetadata(mbucket) 159 | if err != nil { 160 | return err 161 | } 162 | 163 | if metadata.empty() { 164 | return nil 165 | } 166 | 167 | size = (metadata.Last - metadata.Head) + 1 168 | return nil 169 | }) 170 | if err != nil { 171 | return 0, err 172 | } 173 | 174 | return size, nil 175 | } 176 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/internal/redistest" 23 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/redis" 24 | ) 25 | 26 | type valueError struct { 27 | v interface{} 28 | err error 29 | } 30 | 31 | func ve(v interface{}, err error) valueError { 32 | return valueError{v, err} 33 | } 34 | 35 | var replyTests = []struct { 36 | name interface{} 37 | actual valueError 38 | expected valueError 39 | }{ 40 | { 41 | "ints([v1, v2])", 42 | ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)), 43 | ve([]int{4, 5}, nil), 44 | }, 45 | { 46 | "ints(nil)", 47 | ve(redis.Ints(nil, nil)), 48 | ve([]int(nil), redis.ErrNil), 49 | }, 50 | { 51 | "strings([v1, v2])", 52 | ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 53 | ve([]string{"v1", "v2"}, nil), 54 | }, 55 | { 56 | "strings(nil)", 57 | ve(redis.Strings(nil, nil)), 58 | ve([]string(nil), redis.ErrNil), 59 | }, 60 | { 61 | "values([v1, v2])", 62 | ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 63 | ve([]interface{}{[]byte("v1"), []byte("v2")}, nil), 64 | }, 65 | { 66 | "values(nil)", 67 | ve(redis.Values(nil, nil)), 68 | ve([]interface{}(nil), redis.ErrNil), 69 | }, 70 | { 71 | "float64(1.0)", 72 | ve(redis.Float64([]byte("1.0"), nil)), 73 | ve(float64(1.0), nil), 74 | }, 75 | { 76 | "float64(nil)", 77 | ve(redis.Float64(nil, nil)), 78 | ve(float64(0.0), redis.ErrNil), 79 | }, 80 | { 81 | "uint64(1)", 82 | ve(redis.Uint64(int64(1), nil)), 83 | ve(uint64(1), nil), 84 | }, 85 | { 86 | "uint64(-1)", 87 | ve(redis.Uint64(int64(-1), nil)), 88 | ve(uint64(0), redis.ErrNegativeInt), 89 | }, 90 | } 91 | 92 | func TestReply(t *testing.T) { 93 | for _, rt := range replyTests { 94 | if rt.actual.err != rt.expected.err { 95 | t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err) 96 | continue 97 | } 98 | if !reflect.DeepEqual(rt.actual.v, rt.expected.v) { 99 | t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v) 100 | } 101 | } 102 | } 103 | 104 | // dial wraps DialTestDB() with a more suitable function name for examples. 105 | func dial() (redis.Conn, error) { 106 | return redistest.Dial() 107 | } 108 | 109 | func ExampleBool() { 110 | c, err := dial() 111 | if err != nil { 112 | panic(err) 113 | } 114 | defer c.Close() 115 | 116 | c.Do("SET", "foo", 1) 117 | exists, _ := redis.Bool(c.Do("EXISTS", "foo")) 118 | fmt.Printf("%#v\n", exists) 119 | // Output: 120 | // true 121 | } 122 | 123 | func ExampleInt() { 124 | c, err := dial() 125 | if err != nil { 126 | panic(err) 127 | } 128 | defer c.Close() 129 | 130 | c.Do("SET", "k1", 1) 131 | n, _ := redis.Int(c.Do("GET", "k1")) 132 | fmt.Printf("%#v\n", n) 133 | n, _ = redis.Int(c.Do("INCR", "k1")) 134 | fmt.Printf("%#v\n", n) 135 | // Output: 136 | // 1 137 | // 2 138 | } 139 | 140 | func ExampleInts() { 141 | c, err := dial() 142 | if err != nil { 143 | panic(err) 144 | } 145 | defer c.Close() 146 | 147 | c.Do("SADD", "set_with_integers", 4, 5, 6) 148 | ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers")) 149 | fmt.Printf("%#v\n", ints) 150 | // Output: 151 | // []int{4, 5, 6} 152 | } 153 | 154 | func ExampleString() { 155 | c, err := dial() 156 | if err != nil { 157 | panic(err) 158 | } 159 | defer c.Close() 160 | 161 | c.Do("SET", "hello", "world") 162 | s, err := redis.String(c.Do("GET", "hello")) 163 | fmt.Printf("%#v\n", s) 164 | // Output: 165 | // "world" 166 | } 167 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 | "net" 20 | "reflect" 21 | "sync" 22 | "testing" 23 | "time" 24 | 25 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/internal/redistest" 26 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/redis" 27 | ) 28 | 29 | func publish(channel, value interface{}) { 30 | c, err := dial() 31 | if err != nil { 32 | panic(err) 33 | } 34 | defer c.Close() 35 | c.Do("PUBLISH", channel, value) 36 | } 37 | 38 | // Applications can receive pushed messages from one goroutine and manage subscriptions from another goroutine. 39 | func ExamplePubSubConn() { 40 | c, err := dial() 41 | if err != nil { 42 | panic(err) 43 | } 44 | defer c.Close() 45 | var wg sync.WaitGroup 46 | wg.Add(2) 47 | 48 | psc := redis.PubSubConn{Conn: c} 49 | 50 | // This goroutine receives and prints pushed notifications from the server. 51 | // The goroutine exits when the connection is unsubscribed from all 52 | // channels or there is an error. 53 | go func() { 54 | defer wg.Done() 55 | for { 56 | switch n := psc.Receive().(type) { 57 | case redis.Message: 58 | fmt.Printf("Message: %s %s\n", n.Channel, n.Data) 59 | case redis.PMessage: 60 | fmt.Printf("PMessage: %s %s %s\n", n.Pattern, n.Channel, n.Data) 61 | case redis.Subscription: 62 | fmt.Printf("Subscription: %s %s %d\n", n.Kind, n.Channel, n.Count) 63 | if n.Count == 0 { 64 | return 65 | } 66 | case error: 67 | fmt.Printf("error: %v\n", n) 68 | return 69 | } 70 | } 71 | }() 72 | 73 | // This goroutine manages subscriptions for the connection. 74 | go func() { 75 | defer wg.Done() 76 | 77 | psc.Subscribe("example") 78 | psc.PSubscribe("p*") 79 | 80 | // The following function calls publish a message using another 81 | // connection to the Redis server. 82 | publish("example", "hello") 83 | publish("example", "world") 84 | publish("pexample", "foo") 85 | publish("pexample", "bar") 86 | 87 | // Unsubscribe from all connections. This will cause the receiving 88 | // goroutine to exit. 89 | psc.Unsubscribe() 90 | psc.PUnsubscribe() 91 | }() 92 | 93 | wg.Wait() 94 | 95 | // Output: 96 | // Subscription: subscribe example 1 97 | // Subscription: psubscribe p* 2 98 | // Message: example hello 99 | // Message: example world 100 | // PMessage: p* pexample foo 101 | // PMessage: p* pexample bar 102 | // Subscription: unsubscribe example 1 103 | // Subscription: punsubscribe p* 0 104 | } 105 | 106 | func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) { 107 | actual := c.Receive() 108 | if !reflect.DeepEqual(actual, expected) { 109 | t.Errorf("%s = %v, want %v", message, actual, expected) 110 | } 111 | } 112 | 113 | func TestPushed(t *testing.T) { 114 | pc, err := redistest.Dial() 115 | if err != nil { 116 | t.Fatalf("error connection to database, %v", err) 117 | } 118 | defer pc.Close() 119 | 120 | nc, err := net.Dial("tcp", ":6379") 121 | if err != nil { 122 | t.Fatal(err) 123 | } 124 | defer nc.Close() 125 | nc.SetReadDeadline(time.Now().Add(4 * time.Second)) 126 | 127 | c := redis.PubSubConn{Conn: redis.NewConn(nc, 0, 0)} 128 | 129 | c.Subscribe("c1") 130 | expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1}) 131 | c.Subscribe("c2") 132 | expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2}) 133 | c.PSubscribe("p1") 134 | expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3}) 135 | c.PSubscribe("p2") 136 | expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4}) 137 | c.PUnsubscribe() 138 | expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3}) 139 | expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2}) 140 | 141 | pc.Do("PUBLISH", "c1", "hello") 142 | expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")}) 143 | } 144 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "os" 8 | "time" 9 | 10 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 11 | "github.com/codegangsta/cli" 12 | ) 13 | 14 | var branch, commit string 15 | 16 | func main() { 17 | log.SetFlags(0) 18 | NewApp().Run(os.Args) 19 | } 20 | 21 | // NewApp creates an Application instance. 22 | func NewApp() *cli.App { 23 | app := cli.NewApp() 24 | app.Name = "bolt" 25 | app.Usage = "BoltDB toolkit" 26 | app.Version = fmt.Sprintf("0.1.0 (%s %s)", branch, commit) 27 | app.Commands = []cli.Command{ 28 | { 29 | Name: "info", 30 | Usage: "Print basic information about a database", 31 | Action: func(c *cli.Context) { 32 | path := c.Args().Get(0) 33 | Info(path) 34 | }, 35 | }, 36 | { 37 | Name: "get", 38 | Usage: "Retrieve a value for given key in a bucket", 39 | Action: func(c *cli.Context) { 40 | path, name, key := c.Args().Get(0), c.Args().Get(1), c.Args().Get(2) 41 | Get(path, name, key) 42 | }, 43 | }, 44 | { 45 | Name: "keys", 46 | Usage: "Retrieve a list of all keys in a bucket", 47 | Action: func(c *cli.Context) { 48 | path, name := c.Args().Get(0), c.Args().Get(1) 49 | Keys(path, name) 50 | }, 51 | }, 52 | { 53 | Name: "buckets", 54 | Usage: "Retrieves a list of all buckets", 55 | Action: func(c *cli.Context) { 56 | path := c.Args().Get(0) 57 | Buckets(path) 58 | }, 59 | }, 60 | { 61 | Name: "pages", 62 | Usage: "Dumps page information for a database", 63 | Action: func(c *cli.Context) { 64 | path := c.Args().Get(0) 65 | Pages(path) 66 | }, 67 | }, 68 | { 69 | Name: "check", 70 | Usage: "Performs a consistency check on the database", 71 | Action: func(c *cli.Context) { 72 | path := c.Args().Get(0) 73 | Check(path) 74 | }, 75 | }, 76 | { 77 | Name: "stats", 78 | Usage: "Aggregate statistics for all buckets matching specified prefix", 79 | Action: func(c *cli.Context) { 80 | path, name := c.Args().Get(0), c.Args().Get(1) 81 | Stats(path, name) 82 | }, 83 | }, 84 | { 85 | Name: "bench", 86 | Usage: "Performs a synthetic benchmark", 87 | Flags: []cli.Flag{ 88 | &cli.StringFlag{Name: "profile-mode", Value: "rw", Usage: "Profile mode"}, 89 | &cli.StringFlag{Name: "write-mode", Value: "seq", Usage: "Write mode"}, 90 | &cli.StringFlag{Name: "read-mode", Value: "seq", Usage: "Read mode"}, 91 | &cli.IntFlag{Name: "count", Value: 1000, Usage: "Item count"}, 92 | &cli.IntFlag{Name: "batch-size", Usage: "Write batch size"}, 93 | &cli.IntFlag{Name: "key-size", Value: 8, Usage: "Key size"}, 94 | &cli.IntFlag{Name: "value-size", Value: 32, Usage: "Value size"}, 95 | &cli.StringFlag{Name: "cpuprofile", Usage: "CPU profile output path"}, 96 | &cli.StringFlag{Name: "memprofile", Usage: "Memory profile output path"}, 97 | &cli.StringFlag{Name: "blockprofile", Usage: "Block profile output path"}, 98 | &cli.StringFlag{Name: "stats-interval", Value: "0s", Usage: "Continuous stats interval"}, 99 | &cli.Float64Flag{Name: "fill-percent", Value: bolt.DefaultFillPercent, Usage: "Fill percentage"}, 100 | &cli.BoolFlag{Name: "no-sync", Usage: "Skip fsync on every commit"}, 101 | &cli.BoolFlag{Name: "work", Usage: "Print the temp db and do not delete on exit"}, 102 | }, 103 | Action: func(c *cli.Context) { 104 | statsInterval, err := time.ParseDuration(c.String("stats-interval")) 105 | if err != nil { 106 | fatal(err) 107 | } 108 | 109 | Bench(&BenchOptions{ 110 | ProfileMode: c.String("profile-mode"), 111 | WriteMode: c.String("write-mode"), 112 | ReadMode: c.String("read-mode"), 113 | Iterations: c.Int("count"), 114 | BatchSize: c.Int("batch-size"), 115 | KeySize: c.Int("key-size"), 116 | ValueSize: c.Int("value-size"), 117 | CPUProfile: c.String("cpuprofile"), 118 | MemProfile: c.String("memprofile"), 119 | BlockProfile: c.String("blockprofile"), 120 | StatsInterval: statsInterval, 121 | FillPercent: c.Float64("fill-percent"), 122 | NoSync: c.Bool("no-sync"), 123 | Clean: !c.Bool("work"), 124 | }) 125 | }, 126 | }} 127 | return app 128 | } 129 | 130 | var logger = log.New(os.Stderr, "", 0) 131 | var logBuffer *bytes.Buffer 132 | 133 | func print(v ...interface{}) { 134 | if testMode { 135 | logger.Print(v...) 136 | } else { 137 | fmt.Print(v...) 138 | } 139 | } 140 | 141 | func printf(format string, v ...interface{}) { 142 | if testMode { 143 | logger.Printf(format, v...) 144 | } else { 145 | fmt.Printf(format, v...) 146 | } 147 | } 148 | 149 | func println(v ...interface{}) { 150 | if testMode { 151 | logger.Println(v...) 152 | } else { 153 | fmt.Println(v...) 154 | } 155 | } 156 | 157 | func fatal(v ...interface{}) { 158 | logger.Print(v...) 159 | if !testMode { 160 | os.Exit(1) 161 | } 162 | } 163 | 164 | func fatalf(format string, v ...interface{}) { 165 | logger.Printf(format, v...) 166 | if !testMode { 167 | os.Exit(1) 168 | } 169 | } 170 | 171 | func fatalln(v ...interface{}) { 172 | logger.Println(v...) 173 | if !testMode { 174 | os.Exit(1) 175 | } 176 | } 177 | 178 | // LogBuffer returns the contents of the log. 179 | // This only works while the CLI is in test mode. 180 | func LogBuffer() string { 181 | if logBuffer != nil { 182 | return logBuffer.String() 183 | } 184 | return "" 185 | } 186 | 187 | var testMode bool 188 | 189 | // SetTestMode sets whether the CLI is running in test mode and resets the logger. 190 | func SetTestMode(value bool) { 191 | testMode = value 192 | if testMode { 193 | logBuffer = bytes.NewBuffer(nil) 194 | logger = log.New(logBuffer, "", 0) 195 | } else { 196 | logger = log.New(os.Stderr, "", 0) 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/node_test.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "testing" 5 | "unsafe" 6 | ) 7 | 8 | // Ensure that a node can insert a key/value. 9 | func TestNode_put(t *testing.T) { 10 | n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{meta: &meta{pgid: 1}}}} 11 | n.put([]byte("baz"), []byte("baz"), []byte("2"), 0, 0) 12 | n.put([]byte("foo"), []byte("foo"), []byte("0"), 0, 0) 13 | n.put([]byte("bar"), []byte("bar"), []byte("1"), 0, 0) 14 | n.put([]byte("foo"), []byte("foo"), []byte("3"), 0, leafPageFlag) 15 | 16 | if len(n.inodes) != 3 { 17 | t.Fatalf("exp=3; got=%d", len(n.inodes)) 18 | } 19 | if k, v := n.inodes[0].key, n.inodes[0].value; string(k) != "bar" || string(v) != "1" { 20 | t.Fatalf("exp=; got=<%s,%s>", k, v) 21 | } 22 | if k, v := n.inodes[1].key, n.inodes[1].value; string(k) != "baz" || string(v) != "2" { 23 | t.Fatalf("exp=; got=<%s,%s>", k, v) 24 | } 25 | if k, v := n.inodes[2].key, n.inodes[2].value; string(k) != "foo" || string(v) != "3" { 26 | t.Fatalf("exp=; got=<%s,%s>", k, v) 27 | } 28 | if n.inodes[2].flags != uint32(leafPageFlag) { 29 | t.Fatalf("not a leaf: %d", n.inodes[2].flags) 30 | } 31 | } 32 | 33 | // Ensure that a node can deserialize from a leaf page. 34 | func TestNode_read_LeafPage(t *testing.T) { 35 | // Create a page. 36 | var buf [4096]byte 37 | page := (*page)(unsafe.Pointer(&buf[0])) 38 | page.flags = leafPageFlag 39 | page.count = 2 40 | 41 | // Insert 2 elements at the beginning. sizeof(leafPageElement) == 16 42 | nodes := (*[3]leafPageElement)(unsafe.Pointer(&page.ptr)) 43 | nodes[0] = leafPageElement{flags: 0, pos: 32, ksize: 3, vsize: 4} // pos = sizeof(leafPageElement) * 2 44 | nodes[1] = leafPageElement{flags: 0, pos: 23, ksize: 10, vsize: 3} // pos = sizeof(leafPageElement) + 3 + 4 45 | 46 | // Write data for the nodes at the end. 47 | data := (*[4096]byte)(unsafe.Pointer(&nodes[2])) 48 | copy(data[:], []byte("barfooz")) 49 | copy(data[7:], []byte("helloworldbye")) 50 | 51 | // Deserialize page into a leaf. 52 | n := &node{} 53 | n.read(page) 54 | 55 | // Check that there are two inodes with correct data. 56 | if !n.isLeaf { 57 | t.Fatal("expected leaf") 58 | } 59 | if len(n.inodes) != 2 { 60 | t.Fatalf("exp=2; got=%d", len(n.inodes)) 61 | } 62 | if k, v := n.inodes[0].key, n.inodes[0].value; string(k) != "bar" || string(v) != "fooz" { 63 | t.Fatalf("exp=; got=<%s,%s>", k, v) 64 | } 65 | if k, v := n.inodes[1].key, n.inodes[1].value; string(k) != "helloworld" || string(v) != "bye" { 66 | t.Fatalf("exp=; got=<%s,%s>", k, v) 67 | } 68 | } 69 | 70 | // Ensure that a node can serialize into a leaf page. 71 | func TestNode_write_LeafPage(t *testing.T) { 72 | // Create a node. 73 | n := &node{isLeaf: true, inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: &meta{pgid: 1}}}} 74 | n.put([]byte("susy"), []byte("susy"), []byte("que"), 0, 0) 75 | n.put([]byte("ricki"), []byte("ricki"), []byte("lake"), 0, 0) 76 | n.put([]byte("john"), []byte("john"), []byte("johnson"), 0, 0) 77 | 78 | // Write it to a page. 79 | var buf [4096]byte 80 | p := (*page)(unsafe.Pointer(&buf[0])) 81 | n.write(p) 82 | 83 | // Read the page back in. 84 | n2 := &node{} 85 | n2.read(p) 86 | 87 | // Check that the two pages are the same. 88 | if len(n2.inodes) != 3 { 89 | t.Fatalf("exp=3; got=%d", len(n2.inodes)) 90 | } 91 | if k, v := n2.inodes[0].key, n2.inodes[0].value; string(k) != "john" || string(v) != "johnson" { 92 | t.Fatalf("exp=; got=<%s,%s>", k, v) 93 | } 94 | if k, v := n2.inodes[1].key, n2.inodes[1].value; string(k) != "ricki" || string(v) != "lake" { 95 | t.Fatalf("exp=; got=<%s,%s>", k, v) 96 | } 97 | if k, v := n2.inodes[2].key, n2.inodes[2].value; string(k) != "susy" || string(v) != "que" { 98 | t.Fatalf("exp=; got=<%s,%s>", k, v) 99 | } 100 | } 101 | 102 | // Ensure that a node can split into appropriate subgroups. 103 | func TestNode_split(t *testing.T) { 104 | // Create a node. 105 | n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: &meta{pgid: 1}}}} 106 | n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) 107 | n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) 108 | n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0) 109 | n.put([]byte("00000004"), []byte("00000004"), []byte("0123456701234567"), 0, 0) 110 | n.put([]byte("00000005"), []byte("00000005"), []byte("0123456701234567"), 0, 0) 111 | 112 | // Split between 2 & 3. 113 | n.split(100) 114 | 115 | var parent = n.parent 116 | if len(parent.children) != 2 { 117 | t.Fatalf("exp=2; got=%d", len(parent.children)) 118 | } 119 | if len(parent.children[0].inodes) != 2 { 120 | t.Fatalf("exp=2; got=%d", len(parent.children[0].inodes)) 121 | } 122 | if len(parent.children[1].inodes) != 3 { 123 | t.Fatalf("exp=3; got=%d", len(parent.children[1].inodes)) 124 | } 125 | } 126 | 127 | // Ensure that a page with the minimum number of inodes just returns a single node. 128 | func TestNode_split_MinKeys(t *testing.T) { 129 | // Create a node. 130 | n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: &meta{pgid: 1}}}} 131 | n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) 132 | n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) 133 | 134 | // Split. 135 | n.split(20) 136 | if n.parent != nil { 137 | t.Fatalf("expected nil parent") 138 | } 139 | } 140 | 141 | // Ensure that a node that has keys that all fit on a page just returns one leaf. 142 | func TestNode_split_SinglePage(t *testing.T) { 143 | // Create a node. 144 | n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{db: &DB{}, meta: &meta{pgid: 1}}}} 145 | n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0) 146 | n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0) 147 | n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0) 148 | n.put([]byte("00000004"), []byte("00000004"), []byte("0123456701234567"), 0, 0) 149 | n.put([]byte("00000005"), []byte("00000005"), []byte("0123456701234567"), 0, 0) 150 | 151 | // Split. 152 | n.split(4096) 153 | if n.parent != nil { 154 | t.Fatalf("expected nil parent") 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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 170 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/freelist.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | "unsafe" 7 | ) 8 | 9 | // freelist represents a list of all pages that are available for allocation. 10 | // It also tracks pages that have been freed but are still in use by open transactions. 11 | type freelist struct { 12 | ids []pgid // all free and available free page ids. 13 | pending map[txid][]pgid // mapping of soon-to-be free page ids by tx. 14 | cache map[pgid]bool // fast lookup of all free and pending page ids. 15 | } 16 | 17 | // newFreelist returns an empty, initialized freelist. 18 | func newFreelist() *freelist { 19 | return &freelist{ 20 | pending: make(map[txid][]pgid), 21 | cache: make(map[pgid]bool), 22 | } 23 | } 24 | 25 | // size returns the size of the page after serialization. 26 | func (f *freelist) size() int { 27 | return pageHeaderSize + (int(unsafe.Sizeof(pgid(0))) * f.count()) 28 | } 29 | 30 | // count returns count of pages on the freelist 31 | func (f *freelist) count() int { 32 | return f.free_count() + f.pending_count() 33 | } 34 | 35 | // free_count returns count of free pages 36 | func (f *freelist) free_count() int { 37 | return len(f.ids) 38 | } 39 | 40 | // pending_count returns count of pending pages 41 | func (f *freelist) pending_count() int { 42 | var count int 43 | for _, list := range f.pending { 44 | count += len(list) 45 | } 46 | return count 47 | } 48 | 49 | // all returns a list of all free ids and all pending ids in one sorted list. 50 | func (f *freelist) all() []pgid { 51 | ids := make([]pgid, len(f.ids)) 52 | copy(ids, f.ids) 53 | 54 | for _, list := range f.pending { 55 | ids = append(ids, list...) 56 | } 57 | 58 | sort.Sort(pgids(ids)) 59 | return ids 60 | } 61 | 62 | // allocate returns the starting page id of a contiguous list of pages of a given size. 63 | // If a contiguous block cannot be found then 0 is returned. 64 | func (f *freelist) allocate(n int) pgid { 65 | if len(f.ids) == 0 { 66 | return 0 67 | } 68 | 69 | var initial, previd pgid 70 | for i, id := range f.ids { 71 | if id <= 1 { 72 | panic(fmt.Sprintf("invalid page allocation: %d", id)) 73 | } 74 | 75 | // Reset initial page if this is not contiguous. 76 | if previd == 0 || id-previd != 1 { 77 | initial = id 78 | } 79 | 80 | // If we found a contiguous block then remove it and return it. 81 | if (id-initial)+1 == pgid(n) { 82 | // If we're allocating off the beginning then take the fast path 83 | // and just adjust the existing slice. This will use extra memory 84 | // temporarily but the append() in free() will realloc the slice 85 | // as is necessary. 86 | if (i + 1) == n { 87 | f.ids = f.ids[i+1:] 88 | } else { 89 | copy(f.ids[i-n+1:], f.ids[i+1:]) 90 | f.ids = f.ids[:len(f.ids)-n] 91 | } 92 | 93 | // Remove from the free cache. 94 | for i := pgid(0); i < pgid(n); i++ { 95 | delete(f.cache, initial+i) 96 | } 97 | 98 | return initial 99 | } 100 | 101 | previd = id 102 | } 103 | return 0 104 | } 105 | 106 | // free releases a page and its overflow for a given transaction id. 107 | // If the page is already free then a panic will occur. 108 | func (f *freelist) free(txid txid, p *page) { 109 | if p.id <= 1 { 110 | panic(fmt.Sprintf("cannot free page 0 or 1: %d", p.id)) 111 | } 112 | 113 | // Free page and all its overflow pages. 114 | var ids = f.pending[txid] 115 | for id := p.id; id <= p.id+pgid(p.overflow); id++ { 116 | // Verify that page is not already free. 117 | if f.cache[id] { 118 | panic(fmt.Sprintf("page %d already freed", id)) 119 | } 120 | 121 | // Add to the freelist and cache. 122 | ids = append(ids, id) 123 | f.cache[id] = true 124 | } 125 | f.pending[txid] = ids 126 | } 127 | 128 | // release moves all page ids for a transaction id (or older) to the freelist. 129 | func (f *freelist) release(txid txid) { 130 | for tid, ids := range f.pending { 131 | if tid <= txid { 132 | // Move transaction's pending pages to the available freelist. 133 | // Don't remove from the cache since the page is still free. 134 | f.ids = append(f.ids, ids...) 135 | delete(f.pending, tid) 136 | } 137 | } 138 | sort.Sort(pgids(f.ids)) 139 | } 140 | 141 | // rollback removes the pages from a given pending tx. 142 | func (f *freelist) rollback(txid txid) { 143 | // Remove page ids from cache. 144 | for _, id := range f.pending[txid] { 145 | delete(f.cache, id) 146 | } 147 | 148 | // Remove pages from pending list. 149 | delete(f.pending, txid) 150 | } 151 | 152 | // freed returns whether a given page is in the free list. 153 | func (f *freelist) freed(pgid pgid) bool { 154 | return f.cache[pgid] 155 | } 156 | 157 | // read initializes the freelist from a freelist page. 158 | func (f *freelist) read(p *page) { 159 | // If the page.count is at the max uint16 value (64k) then it's considered 160 | // an overflow and the size of the freelist is stored as the first element. 161 | idx, count := 0, int(p.count) 162 | if count == 0xFFFF { 163 | idx = 1 164 | count = int(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[0]) 165 | } 166 | 167 | // Copy the list of page ids from the freelist. 168 | ids := ((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[idx:count] 169 | f.ids = make([]pgid, len(ids)) 170 | copy(f.ids, ids) 171 | 172 | // Make sure they're sorted. 173 | sort.Sort(pgids(f.ids)) 174 | 175 | // Rebuild the page cache. 176 | f.reindex() 177 | } 178 | 179 | // write writes the page ids onto a freelist page. All free and pending ids are 180 | // saved to disk since in the event of a program crash, all pending ids will 181 | // become free. 182 | func (f *freelist) write(p *page) error { 183 | // Combine the old free pgids and pgids waiting on an open transaction. 184 | ids := f.all() 185 | 186 | // Update the header flag. 187 | p.flags |= freelistPageFlag 188 | 189 | // The page.count can only hold up to 64k elements so if we overflow that 190 | // number then we handle it by putting the size in the first element. 191 | if len(ids) < 0xFFFF { 192 | p.count = uint16(len(ids)) 193 | copy(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[:], ids) 194 | } else { 195 | p.count = 0xFFFF 196 | ((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[0] = pgid(len(ids)) 197 | copy(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[1:], ids) 198 | } 199 | 200 | return nil 201 | } 202 | 203 | // reload reads the freelist from a page and filters out pending items. 204 | func (f *freelist) reload(p *page) { 205 | f.read(p) 206 | 207 | // Build a cache of only pending pages. 208 | pcache := make(map[pgid]bool) 209 | for _, pendingIDs := range f.pending { 210 | for _, pendingID := range pendingIDs { 211 | pcache[pendingID] = true 212 | } 213 | } 214 | 215 | // Check each page in the freelist and build a new available freelist 216 | // with any pages not in the pending lists. 217 | var a []pgid 218 | for _, id := range f.ids { 219 | if !pcache[id] { 220 | a = append(a, id) 221 | } 222 | } 223 | f.ids = a 224 | 225 | // Once the available list is rebuilt then rebuild the free cache so that 226 | // it includes the available and pending free pages. 227 | f.reindex() 228 | } 229 | 230 | // reindex rebuilds the free cache based on available and pending free lists. 231 | func (f *freelist) reindex() { 232 | f.cache = make(map[pgid]bool) 233 | for _, id := range f.ids { 234 | f.cache[id] = true 235 | } 236 | for _, pendingIDs := range f.pending { 237 | for _, pendingID := range pendingIDs { 238 | f.cache[pendingID] = true 239 | } 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/simulation_test.go: -------------------------------------------------------------------------------- 1 | package bolt_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math/rand" 7 | "sync" 8 | "testing" 9 | 10 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 11 | ) 12 | 13 | func TestSimulate_1op_1p(t *testing.T) { testSimulate(t, 100, 1) } 14 | func TestSimulate_10op_1p(t *testing.T) { testSimulate(t, 10, 1) } 15 | func TestSimulate_100op_1p(t *testing.T) { testSimulate(t, 100, 1) } 16 | func TestSimulate_1000op_1p(t *testing.T) { testSimulate(t, 1000, 1) } 17 | func TestSimulate_10000op_1p(t *testing.T) { testSimulate(t, 10000, 1) } 18 | 19 | func TestSimulate_10op_10p(t *testing.T) { testSimulate(t, 10, 10) } 20 | func TestSimulate_100op_10p(t *testing.T) { testSimulate(t, 100, 10) } 21 | func TestSimulate_1000op_10p(t *testing.T) { testSimulate(t, 1000, 10) } 22 | func TestSimulate_10000op_10p(t *testing.T) { testSimulate(t, 10000, 10) } 23 | 24 | func TestSimulate_100op_100p(t *testing.T) { testSimulate(t, 100, 100) } 25 | func TestSimulate_1000op_100p(t *testing.T) { testSimulate(t, 1000, 100) } 26 | func TestSimulate_10000op_100p(t *testing.T) { testSimulate(t, 10000, 100) } 27 | 28 | func TestSimulate_10000op_1000p(t *testing.T) { testSimulate(t, 10000, 1000) } 29 | 30 | // Randomly generate operations on a given database with multiple clients to ensure consistency and thread safety. 31 | func testSimulate(t *testing.T, threadCount, parallelism int) { 32 | if testing.Short() { 33 | t.Skip("skipping test in short mode.") 34 | } 35 | 36 | rand.Seed(int64(qseed)) 37 | 38 | // A list of operations that readers and writers can perform. 39 | var readerHandlers = []simulateHandler{simulateGetHandler} 40 | var writerHandlers = []simulateHandler{simulateGetHandler, simulatePutHandler} 41 | 42 | var versions = make(map[int]*QuickDB) 43 | versions[1] = NewQuickDB() 44 | 45 | db := NewTestDB() 46 | defer db.Close() 47 | 48 | var mutex sync.Mutex 49 | 50 | // Run n threads in parallel, each with their own operation. 51 | var wg sync.WaitGroup 52 | var threads = make(chan bool, parallelism) 53 | var i int 54 | for { 55 | threads <- true 56 | wg.Add(1) 57 | writable := ((rand.Int() % 100) < 20) // 20% writers 58 | 59 | // Choose an operation to execute. 60 | var handler simulateHandler 61 | if writable { 62 | handler = writerHandlers[rand.Intn(len(writerHandlers))] 63 | } else { 64 | handler = readerHandlers[rand.Intn(len(readerHandlers))] 65 | } 66 | 67 | // Execute a thread for the given operation. 68 | go func(writable bool, handler simulateHandler) { 69 | defer wg.Done() 70 | 71 | // Start transaction. 72 | tx, err := db.Begin(writable) 73 | if err != nil { 74 | t.Fatal("tx begin: ", err) 75 | } 76 | 77 | // Obtain current state of the dataset. 78 | mutex.Lock() 79 | var qdb = versions[tx.ID()] 80 | if writable { 81 | qdb = versions[tx.ID()-1].Copy() 82 | } 83 | mutex.Unlock() 84 | 85 | // Make sure we commit/rollback the tx at the end and update the state. 86 | if writable { 87 | defer func() { 88 | mutex.Lock() 89 | versions[tx.ID()] = qdb 90 | mutex.Unlock() 91 | 92 | ok(t, tx.Commit()) 93 | }() 94 | } else { 95 | defer tx.Rollback() 96 | } 97 | 98 | // Ignore operation if we don't have data yet. 99 | if qdb == nil { 100 | return 101 | } 102 | 103 | // Execute handler. 104 | handler(tx, qdb) 105 | 106 | // Release a thread back to the scheduling loop. 107 | <-threads 108 | }(writable, handler) 109 | 110 | i++ 111 | if i > threadCount { 112 | break 113 | } 114 | } 115 | 116 | // Wait until all threads are done. 117 | wg.Wait() 118 | } 119 | 120 | type simulateHandler func(tx *bolt.Tx, qdb *QuickDB) 121 | 122 | // Retrieves a key from the database and verifies that it is what is expected. 123 | func simulateGetHandler(tx *bolt.Tx, qdb *QuickDB) { 124 | // Randomly retrieve an existing exist. 125 | keys := qdb.Rand() 126 | if len(keys) == 0 { 127 | return 128 | } 129 | 130 | // Retrieve root bucket. 131 | b := tx.Bucket(keys[0]) 132 | if b == nil { 133 | panic(fmt.Sprintf("bucket[0] expected: %08x\n", trunc(keys[0], 4))) 134 | } 135 | 136 | // Drill into nested buckets. 137 | for _, key := range keys[1 : len(keys)-1] { 138 | b = b.Bucket(key) 139 | if b == nil { 140 | panic(fmt.Sprintf("bucket[n] expected: %v -> %v\n", keys, key)) 141 | } 142 | } 143 | 144 | // Verify key/value on the final bucket. 145 | expected := qdb.Get(keys) 146 | actual := b.Get(keys[len(keys)-1]) 147 | if !bytes.Equal(actual, expected) { 148 | fmt.Println("=== EXPECTED ===") 149 | fmt.Println(expected) 150 | fmt.Println("=== ACTUAL ===") 151 | fmt.Println(actual) 152 | fmt.Println("=== END ===") 153 | panic("value mismatch") 154 | } 155 | } 156 | 157 | // Inserts a key into the database. 158 | func simulatePutHandler(tx *bolt.Tx, qdb *QuickDB) { 159 | var err error 160 | keys, value := randKeys(), randValue() 161 | 162 | // Retrieve root bucket. 163 | b := tx.Bucket(keys[0]) 164 | if b == nil { 165 | b, err = tx.CreateBucket(keys[0]) 166 | if err != nil { 167 | panic("create bucket: " + err.Error()) 168 | } 169 | } 170 | 171 | // Create nested buckets, if necessary. 172 | for _, key := range keys[1 : len(keys)-1] { 173 | child := b.Bucket(key) 174 | if child != nil { 175 | b = child 176 | } else { 177 | b, err = b.CreateBucket(key) 178 | if err != nil { 179 | panic("create bucket: " + err.Error()) 180 | } 181 | } 182 | } 183 | 184 | // Insert into database. 185 | if err := b.Put(keys[len(keys)-1], value); err != nil { 186 | panic("put: " + err.Error()) 187 | } 188 | 189 | // Insert into in-memory database. 190 | qdb.Put(keys, value) 191 | } 192 | 193 | // QuickDB is an in-memory database that replicates the functionality of the 194 | // Bolt DB type except that it is entirely in-memory. It is meant for testing 195 | // that the Bolt database is consistent. 196 | type QuickDB struct { 197 | sync.RWMutex 198 | m map[string]interface{} 199 | } 200 | 201 | // NewQuickDB returns an instance of QuickDB. 202 | func NewQuickDB() *QuickDB { 203 | return &QuickDB{m: make(map[string]interface{})} 204 | } 205 | 206 | // Get retrieves the value at a key path. 207 | func (db *QuickDB) Get(keys [][]byte) []byte { 208 | db.RLock() 209 | defer db.RUnlock() 210 | 211 | m := db.m 212 | for _, key := range keys[:len(keys)-1] { 213 | value := m[string(key)] 214 | if value == nil { 215 | return nil 216 | } 217 | switch value := value.(type) { 218 | case map[string]interface{}: 219 | m = value 220 | case []byte: 221 | return nil 222 | } 223 | } 224 | 225 | // Only return if it's a simple value. 226 | if value, ok := m[string(keys[len(keys)-1])].([]byte); ok { 227 | return value 228 | } 229 | return nil 230 | } 231 | 232 | // Put inserts a value into a key path. 233 | func (db *QuickDB) Put(keys [][]byte, value []byte) { 234 | db.Lock() 235 | defer db.Unlock() 236 | 237 | // Build buckets all the way down the key path. 238 | m := db.m 239 | for _, key := range keys[:len(keys)-1] { 240 | if _, ok := m[string(key)].([]byte); ok { 241 | return // Keypath intersects with a simple value. Do nothing. 242 | } 243 | 244 | if m[string(key)] == nil { 245 | m[string(key)] = make(map[string]interface{}) 246 | } 247 | m = m[string(key)].(map[string]interface{}) 248 | } 249 | 250 | // Insert value into the last key. 251 | m[string(keys[len(keys)-1])] = value 252 | } 253 | 254 | // Rand returns a random key path that points to a simple value. 255 | func (db *QuickDB) Rand() [][]byte { 256 | db.RLock() 257 | defer db.RUnlock() 258 | if len(db.m) == 0 { 259 | return nil 260 | } 261 | var keys [][]byte 262 | db.rand(db.m, &keys) 263 | return keys 264 | } 265 | 266 | func (db *QuickDB) rand(m map[string]interface{}, keys *[][]byte) { 267 | i, index := 0, rand.Intn(len(m)) 268 | for k, v := range m { 269 | if i == index { 270 | *keys = append(*keys, []byte(k)) 271 | if v, ok := v.(map[string]interface{}); ok { 272 | db.rand(v, keys) 273 | } 274 | return 275 | } 276 | i++ 277 | } 278 | panic("quickdb rand: out-of-range") 279 | } 280 | 281 | // Copy copies the entire database. 282 | func (db *QuickDB) Copy() *QuickDB { 283 | db.RLock() 284 | defer db.RUnlock() 285 | return &QuickDB{m: db.copy(db.m)} 286 | } 287 | 288 | func (db *QuickDB) copy(m map[string]interface{}) map[string]interface{} { 289 | clone := make(map[string]interface{}, len(m)) 290 | for k, v := range m { 291 | switch v := v.(type) { 292 | case map[string]interface{}: 293 | clone[k] = db.copy(v) 294 | default: 295 | clone[k] = v 296 | } 297 | } 298 | return clone 299 | } 300 | 301 | func randKey() []byte { 302 | var min, max = 1, 1024 303 | n := rand.Intn(max-min) + min 304 | b := make([]byte, n) 305 | for i := 0; i < n; i++ { 306 | b[i] = byte(rand.Intn(255)) 307 | } 308 | return b 309 | } 310 | 311 | func randKeys() [][]byte { 312 | var keys [][]byte 313 | var count = rand.Intn(2) + 2 314 | for i := 0; i < count; i++ { 315 | keys = append(keys, randKey()) 316 | } 317 | return keys 318 | } 319 | 320 | func randValue() []byte { 321 | n := rand.Intn(8192) 322 | b := make([]byte, n) 323 | for i := 0; i < n; i++ { 324 | b[i] = byte(rand.Intn(255)) 325 | } 326 | return b 327 | } 328 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/garyburd/redigo/redis/reply.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 | "errors" 19 | "fmt" 20 | "strconv" 21 | ) 22 | 23 | // ErrNil indicates that a reply value is nil. 24 | var ErrNil = errors.New("redigo: nil returned") 25 | 26 | // Int is a helper that converts a command reply to an integer. If err is not 27 | // equal to nil, then Int returns 0, err. Otherwise, Int converts the 28 | // reply to an int as follows: 29 | // 30 | // Reply type Result 31 | // integer int(reply), nil 32 | // bulk string parsed reply, nil 33 | // nil 0, ErrNil 34 | // other 0, error 35 | func Int(reply interface{}, err error) (int, error) { 36 | if err != nil { 37 | return 0, err 38 | } 39 | switch reply := reply.(type) { 40 | case int64: 41 | x := int(reply) 42 | if int64(x) != reply { 43 | return 0, strconv.ErrRange 44 | } 45 | return x, nil 46 | case []byte: 47 | n, err := strconv.ParseInt(string(reply), 10, 0) 48 | return int(n), err 49 | case nil: 50 | return 0, ErrNil 51 | case Error: 52 | return 0, reply 53 | } 54 | return 0, fmt.Errorf("redigo: unexpected type for Int, got type %T", reply) 55 | } 56 | 57 | // Int64 is a helper that converts a command reply to 64 bit integer. If err is 58 | // not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the 59 | // reply to an int64 as follows: 60 | // 61 | // Reply type Result 62 | // integer reply, nil 63 | // bulk string parsed reply, nil 64 | // nil 0, ErrNil 65 | // other 0, error 66 | func Int64(reply interface{}, err error) (int64, error) { 67 | if err != nil { 68 | return 0, err 69 | } 70 | switch reply := reply.(type) { 71 | case int64: 72 | return reply, nil 73 | case []byte: 74 | n, err := strconv.ParseInt(string(reply), 10, 64) 75 | return n, err 76 | case nil: 77 | return 0, ErrNil 78 | case Error: 79 | return 0, reply 80 | } 81 | return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply) 82 | } 83 | 84 | var errNegativeInt = errors.New("redigo: unexpected value for Uint64") 85 | 86 | // Uint64 is a helper that converts a command reply to 64 bit integer. If err is 87 | // not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the 88 | // reply to an int64 as follows: 89 | // 90 | // Reply type Result 91 | // integer reply, nil 92 | // bulk string parsed reply, nil 93 | // nil 0, ErrNil 94 | // other 0, error 95 | func Uint64(reply interface{}, err error) (uint64, error) { 96 | if err != nil { 97 | return 0, err 98 | } 99 | switch reply := reply.(type) { 100 | case int64: 101 | if reply < 0 { 102 | return 0, errNegativeInt 103 | } 104 | return uint64(reply), nil 105 | case []byte: 106 | n, err := strconv.ParseUint(string(reply), 10, 64) 107 | return n, err 108 | case nil: 109 | return 0, ErrNil 110 | case Error: 111 | return 0, reply 112 | } 113 | return 0, fmt.Errorf("redigo: unexpected type for Uint64, got type %T", reply) 114 | } 115 | 116 | // Float64 is a helper that converts a command reply to 64 bit float. If err is 117 | // not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts 118 | // the reply to an int as follows: 119 | // 120 | // Reply type Result 121 | // bulk string parsed reply, nil 122 | // nil 0, ErrNil 123 | // other 0, error 124 | func Float64(reply interface{}, err error) (float64, error) { 125 | if err != nil { 126 | return 0, err 127 | } 128 | switch reply := reply.(type) { 129 | case []byte: 130 | n, err := strconv.ParseFloat(string(reply), 64) 131 | return n, err 132 | case nil: 133 | return 0, ErrNil 134 | case Error: 135 | return 0, reply 136 | } 137 | return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply) 138 | } 139 | 140 | // String is a helper that converts a command reply to a string. If err is not 141 | // equal to nil, then String returns "", err. Otherwise String converts the 142 | // reply to a string as follows: 143 | // 144 | // Reply type Result 145 | // bulk string string(reply), nil 146 | // simple string reply, nil 147 | // nil "", ErrNil 148 | // other "", error 149 | func String(reply interface{}, err error) (string, error) { 150 | if err != nil { 151 | return "", err 152 | } 153 | switch reply := reply.(type) { 154 | case []byte: 155 | return string(reply), nil 156 | case string: 157 | return reply, nil 158 | case nil: 159 | return "", ErrNil 160 | case Error: 161 | return "", reply 162 | } 163 | return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply) 164 | } 165 | 166 | // Bytes is a helper that converts a command reply to a slice of bytes. If err 167 | // is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts 168 | // the reply to a slice of bytes as follows: 169 | // 170 | // Reply type Result 171 | // bulk string reply, nil 172 | // simple string []byte(reply), nil 173 | // nil nil, ErrNil 174 | // other nil, error 175 | func Bytes(reply interface{}, err error) ([]byte, error) { 176 | if err != nil { 177 | return nil, err 178 | } 179 | switch reply := reply.(type) { 180 | case []byte: 181 | return reply, nil 182 | case string: 183 | return []byte(reply), nil 184 | case nil: 185 | return nil, ErrNil 186 | case Error: 187 | return nil, reply 188 | } 189 | return nil, fmt.Errorf("redigo: unexpected type for Bytes, got type %T", reply) 190 | } 191 | 192 | // Bool is a helper that converts a command reply to a boolean. If err is not 193 | // equal to nil, then Bool returns false, err. Otherwise Bool converts the 194 | // reply to boolean as follows: 195 | // 196 | // Reply type Result 197 | // integer value != 0, nil 198 | // bulk string strconv.ParseBool(reply) 199 | // nil false, ErrNil 200 | // other false, error 201 | func Bool(reply interface{}, err error) (bool, error) { 202 | if err != nil { 203 | return false, err 204 | } 205 | switch reply := reply.(type) { 206 | case int64: 207 | return reply != 0, nil 208 | case []byte: 209 | return strconv.ParseBool(string(reply)) 210 | case nil: 211 | return false, ErrNil 212 | case Error: 213 | return false, reply 214 | } 215 | return false, fmt.Errorf("redigo: unexpected type for Bool, got type %T", reply) 216 | } 217 | 218 | // MultiBulk is deprecated. Use Values. 219 | func MultiBulk(reply interface{}, err error) ([]interface{}, error) { return Values(reply, err) } 220 | 221 | // Values is a helper that converts an array command reply to a []interface{}. 222 | // If err is not equal to nil, then Values returns nil, err. Otherwise, Values 223 | // converts the reply as follows: 224 | // 225 | // Reply type Result 226 | // array reply, nil 227 | // nil nil, ErrNil 228 | // other nil, error 229 | func Values(reply interface{}, err error) ([]interface{}, error) { 230 | if err != nil { 231 | return nil, err 232 | } 233 | switch reply := reply.(type) { 234 | case []interface{}: 235 | return reply, nil 236 | case nil: 237 | return nil, ErrNil 238 | case Error: 239 | return nil, reply 240 | } 241 | return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply) 242 | } 243 | 244 | // Strings is a helper that converts an array command reply to a []string. If 245 | // err is not equal to nil, then Strings returns nil, err. Nil array items are 246 | // converted to "" in the output slice. Strings returns an error if an array 247 | // item is not a bulk string or nil. 248 | func Strings(reply interface{}, err error) ([]string, error) { 249 | if err != nil { 250 | return nil, err 251 | } 252 | switch reply := reply.(type) { 253 | case []interface{}: 254 | result := make([]string, len(reply)) 255 | for i := range reply { 256 | if reply[i] == nil { 257 | continue 258 | } 259 | p, ok := reply[i].([]byte) 260 | if !ok { 261 | return nil, fmt.Errorf("redigo: unexpected element type for Strings, got type %T", reply[i]) 262 | } 263 | result[i] = string(p) 264 | } 265 | return result, nil 266 | case nil: 267 | return nil, ErrNil 268 | case Error: 269 | return nil, reply 270 | } 271 | return nil, fmt.Errorf("redigo: unexpected type for Strings, got type %T", reply) 272 | } 273 | 274 | // Ints is a helper that converts an array command reply to a []int. If 275 | // err is not equal to nil, then Ints returns nil, err. 276 | func Ints(reply interface{}, err error) ([]int, error) { 277 | var ints []int 278 | if reply == nil { 279 | return ints, ErrNil 280 | } 281 | values, err := Values(reply, err) 282 | if err != nil { 283 | return ints, err 284 | } 285 | if err := ScanSlice(values, &ints); err != nil { 286 | return ints, err 287 | } 288 | return ints, nil 289 | } 290 | 291 | // StringMap is a helper that converts an array of strings (alternating key, value) 292 | // into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format. 293 | // Requires an even number of values in result. 294 | func StringMap(result interface{}, err error) (map[string]string, error) { 295 | values, err := Values(result, err) 296 | if err != nil { 297 | return nil, err 298 | } 299 | if len(values)%2 != 0 { 300 | return nil, errors.New("redigo: StringMap expects even number of values result") 301 | } 302 | m := make(map[string]string, len(values)/2) 303 | for i := 0; i < len(values); i += 2 { 304 | key, okKey := values[i].([]byte) 305 | value, okValue := values[i+1].([]byte) 306 | if !okKey || !okValue { 307 | return nil, errors.New("redigo: ScanMap key not a bulk string value") 308 | } 309 | m[string(key)] = string(value) 310 | } 311 | return m, nil 312 | } 313 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/garyburd/redigo/redis/scan_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 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/redis" 20 | "math" 21 | "reflect" 22 | "testing" 23 | ) 24 | 25 | var scanConversionTests = []struct { 26 | src interface{} 27 | dest interface{} 28 | }{ 29 | {[]byte("-inf"), math.Inf(-1)}, 30 | {[]byte("+inf"), math.Inf(1)}, 31 | {[]byte("0"), float64(0)}, 32 | {[]byte("3.14159"), float64(3.14159)}, 33 | {[]byte("3.14"), float32(3.14)}, 34 | {[]byte("-100"), int(-100)}, 35 | {[]byte("101"), int(101)}, 36 | {int64(102), int(102)}, 37 | {[]byte("103"), uint(103)}, 38 | {int64(104), uint(104)}, 39 | {[]byte("105"), int8(105)}, 40 | {int64(106), int8(106)}, 41 | {[]byte("107"), uint8(107)}, 42 | {int64(108), uint8(108)}, 43 | {[]byte("0"), false}, 44 | {int64(0), false}, 45 | {[]byte("f"), false}, 46 | {[]byte("1"), true}, 47 | {int64(1), true}, 48 | {[]byte("t"), true}, 49 | {[]byte("hello"), "hello"}, 50 | {[]byte("world"), []byte("world")}, 51 | {[]interface{}{[]byte("foo")}, []interface{}{[]byte("foo")}}, 52 | {[]interface{}{[]byte("foo")}, []string{"foo"}}, 53 | {[]interface{}{[]byte("hello"), []byte("world")}, []string{"hello", "world"}}, 54 | {[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}}, 55 | {[]interface{}{[]byte("1")}, []int{1}}, 56 | {[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}}, 57 | {[]interface{}{[]byte("1"), []byte("2")}, []float64{1, 2}}, 58 | {[]interface{}{[]byte("1")}, []byte{1}}, 59 | {[]interface{}{[]byte("1")}, []bool{true}}, 60 | } 61 | 62 | func TestScanConversion(t *testing.T) { 63 | for _, tt := range scanConversionTests { 64 | values := []interface{}{tt.src} 65 | dest := reflect.New(reflect.TypeOf(tt.dest)) 66 | values, err := redis.Scan(values, dest.Interface()) 67 | if err != nil { 68 | t.Errorf("Scan(%v) returned error %v", tt, err) 69 | continue 70 | } 71 | if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) { 72 | t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest) 73 | } 74 | } 75 | } 76 | 77 | var scanConversionErrorTests = []struct { 78 | src interface{} 79 | dest interface{} 80 | }{ 81 | {[]byte("1234"), byte(0)}, 82 | {int64(1234), byte(0)}, 83 | {[]byte("-1"), byte(0)}, 84 | {int64(-1), byte(0)}, 85 | {[]byte("junk"), false}, 86 | {redis.Error("blah"), false}, 87 | } 88 | 89 | func TestScanConversionError(t *testing.T) { 90 | for _, tt := range scanConversionErrorTests { 91 | values := []interface{}{tt.src} 92 | dest := reflect.New(reflect.TypeOf(tt.dest)) 93 | values, err := redis.Scan(values, dest.Interface()) 94 | if err == nil { 95 | t.Errorf("Scan(%v) did not return error", tt) 96 | } 97 | } 98 | } 99 | 100 | func ExampleScan() { 101 | c, err := dial() 102 | if err != nil { 103 | panic(err) 104 | } 105 | defer c.Close() 106 | 107 | c.Send("HMSET", "album:1", "title", "Red", "rating", 5) 108 | c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1) 109 | c.Send("HMSET", "album:3", "title", "Beat") 110 | c.Send("LPUSH", "albums", "1") 111 | c.Send("LPUSH", "albums", "2") 112 | c.Send("LPUSH", "albums", "3") 113 | values, err := redis.Values(c.Do("SORT", "albums", 114 | "BY", "album:*->rating", 115 | "GET", "album:*->title", 116 | "GET", "album:*->rating")) 117 | if err != nil { 118 | panic(err) 119 | } 120 | 121 | for len(values) > 0 { 122 | var title string 123 | rating := -1 // initialize to illegal value to detect nil. 124 | values, err = redis.Scan(values, &title, &rating) 125 | if err != nil { 126 | panic(err) 127 | } 128 | if rating == -1 { 129 | fmt.Println(title, "not-rated") 130 | } else { 131 | fmt.Println(title, rating) 132 | } 133 | } 134 | // Output: 135 | // Beat not-rated 136 | // Earthbound 1 137 | // Red 5 138 | } 139 | 140 | type s0 struct { 141 | X int 142 | Y int `redis:"y"` 143 | Bt bool 144 | } 145 | 146 | type s1 struct { 147 | X int `redis:"-"` 148 | I int `redis:"i"` 149 | U uint `redis:"u"` 150 | S string `redis:"s"` 151 | P []byte `redis:"p"` 152 | B bool `redis:"b"` 153 | Bt bool 154 | Bf bool 155 | s0 156 | } 157 | 158 | var scanStructTests = []struct { 159 | title string 160 | reply []string 161 | value interface{} 162 | }{ 163 | {"basic", 164 | []string{"i", "-1234", "u", "5678", "s", "hello", "p", "world", "b", "t", "Bt", "1", "Bf", "0", "X", "123", "y", "456"}, 165 | &s1{I: -1234, U: 5678, S: "hello", P: []byte("world"), B: true, Bt: true, Bf: false, s0: s0{X: 123, Y: 456}}, 166 | }, 167 | } 168 | 169 | func TestScanStruct(t *testing.T) { 170 | for _, tt := range scanStructTests { 171 | 172 | var reply []interface{} 173 | for _, v := range tt.reply { 174 | reply = append(reply, []byte(v)) 175 | } 176 | 177 | value := reflect.New(reflect.ValueOf(tt.value).Type().Elem()) 178 | 179 | if err := redis.ScanStruct(reply, value.Interface()); err != nil { 180 | t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err) 181 | } 182 | 183 | if !reflect.DeepEqual(value.Interface(), tt.value) { 184 | t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value) 185 | } 186 | } 187 | } 188 | 189 | func TestBadScanStructArgs(t *testing.T) { 190 | x := []interface{}{"A", "b"} 191 | test := func(v interface{}) { 192 | if err := redis.ScanStruct(x, v); err == nil { 193 | t.Errorf("Expect error for ScanStruct(%T, %T)", x, v) 194 | } 195 | } 196 | 197 | test(nil) 198 | 199 | var v0 *struct{} 200 | test(v0) 201 | 202 | var v1 int 203 | test(&v1) 204 | 205 | x = x[:1] 206 | v2 := struct{ A string }{} 207 | test(&v2) 208 | } 209 | 210 | var scanSliceTests = []struct { 211 | src []interface{} 212 | fieldNames []string 213 | ok bool 214 | dest interface{} 215 | }{ 216 | { 217 | []interface{}{[]byte("1"), nil, []byte("-1")}, 218 | nil, 219 | true, 220 | []int{1, 0, -1}, 221 | }, 222 | { 223 | []interface{}{[]byte("1"), nil, []byte("2")}, 224 | nil, 225 | true, 226 | []uint{1, 0, 2}, 227 | }, 228 | { 229 | []interface{}{[]byte("-1")}, 230 | nil, 231 | false, 232 | []uint{1}, 233 | }, 234 | { 235 | []interface{}{[]byte("hello"), nil, []byte("world")}, 236 | nil, 237 | true, 238 | [][]byte{[]byte("hello"), nil, []byte("world")}, 239 | }, 240 | { 241 | []interface{}{[]byte("hello"), nil, []byte("world")}, 242 | nil, 243 | true, 244 | []string{"hello", "", "world"}, 245 | }, 246 | { 247 | []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, 248 | nil, 249 | true, 250 | []struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}}, 251 | }, 252 | { 253 | []interface{}{[]byte("a1"), []byte("b1")}, 254 | nil, 255 | false, 256 | []struct{ A, B, C string }{{"a1", "b1", ""}}, 257 | }, 258 | { 259 | []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, 260 | nil, 261 | true, 262 | []*struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}}, 263 | }, 264 | { 265 | []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, 266 | []string{"A", "B"}, 267 | true, 268 | []struct{ A, C, B string }{{"a1", "", "b1"}, {"a2", "", "b2"}}, 269 | }, 270 | { 271 | []interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")}, 272 | nil, 273 | false, 274 | []struct{}{}, 275 | }, 276 | } 277 | 278 | func TestScanSlice(t *testing.T) { 279 | for _, tt := range scanSliceTests { 280 | 281 | typ := reflect.ValueOf(tt.dest).Type() 282 | dest := reflect.New(typ) 283 | 284 | err := redis.ScanSlice(tt.src, dest.Interface(), tt.fieldNames...) 285 | if tt.ok != (err == nil) { 286 | t.Errorf("ScanSlice(%v, []%s, %v) returned error %v", tt.src, typ, tt.fieldNames, err) 287 | continue 288 | } 289 | if tt.ok && !reflect.DeepEqual(dest.Elem().Interface(), tt.dest) { 290 | t.Errorf("ScanSlice(src, []%s) returned %#v, want %#v", typ, dest.Elem().Interface(), tt.dest) 291 | } 292 | } 293 | } 294 | 295 | func ExampleScanSlice() { 296 | c, err := dial() 297 | if err != nil { 298 | panic(err) 299 | } 300 | defer c.Close() 301 | 302 | c.Send("HMSET", "album:1", "title", "Red", "rating", 5) 303 | c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1) 304 | c.Send("HMSET", "album:3", "title", "Beat", "rating", 4) 305 | c.Send("LPUSH", "albums", "1") 306 | c.Send("LPUSH", "albums", "2") 307 | c.Send("LPUSH", "albums", "3") 308 | values, err := redis.Values(c.Do("SORT", "albums", 309 | "BY", "album:*->rating", 310 | "GET", "album:*->title", 311 | "GET", "album:*->rating")) 312 | if err != nil { 313 | panic(err) 314 | } 315 | 316 | var albums []struct { 317 | Title string 318 | Rating int 319 | } 320 | if err := redis.ScanSlice(values, &albums); err != nil { 321 | panic(err) 322 | } 323 | fmt.Printf("%v\n", albums) 324 | // Output: 325 | // [{Earthbound 1} {Beat 4} {Red 5}] 326 | } 327 | 328 | var argsTests = []struct { 329 | title string 330 | actual redis.Args 331 | expected redis.Args 332 | }{ 333 | {"struct ptr", 334 | redis.Args{}.AddFlat(&struct { 335 | I int `redis:"i"` 336 | U uint `redis:"u"` 337 | S string `redis:"s"` 338 | P []byte `redis:"p"` 339 | Bt bool 340 | Bf bool 341 | }{ 342 | -1234, 5678, "hello", []byte("world"), true, false, 343 | }), 344 | redis.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "Bt", true, "Bf", false}, 345 | }, 346 | {"struct", 347 | redis.Args{}.AddFlat(struct{ I int }{123}), 348 | redis.Args{"I", 123}, 349 | }, 350 | {"slice", 351 | redis.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2), 352 | redis.Args{1, "a", "b", "c", 2}, 353 | }, 354 | } 355 | 356 | func TestArgs(t *testing.T) { 357 | for _, tt := range argsTests { 358 | if !reflect.DeepEqual(tt.actual, tt.expected) { 359 | t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected) 360 | } 361 | } 362 | } 363 | 364 | func ExampleArgs() { 365 | c, err := dial() 366 | if err != nil { 367 | panic(err) 368 | } 369 | defer c.Close() 370 | 371 | var p1, p2 struct { 372 | Title string `redis:"title"` 373 | Author string `redis:"author"` 374 | Body string `redis:"body"` 375 | } 376 | 377 | p1.Title = "Example" 378 | p1.Author = "Gary" 379 | p1.Body = "Hello" 380 | 381 | if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil { 382 | panic(err) 383 | } 384 | 385 | m := map[string]string{ 386 | "title": "Example2", 387 | "author": "Steve", 388 | "body": "Map", 389 | } 390 | 391 | if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil { 392 | panic(err) 393 | } 394 | 395 | for _, id := range []string{"id1", "id2"} { 396 | 397 | v, err := redis.Values(c.Do("HGETALL", id)) 398 | if err != nil { 399 | panic(err) 400 | } 401 | 402 | if err := redis.ScanStruct(v, &p2); err != nil { 403 | panic(err) 404 | } 405 | 406 | fmt.Printf("%+v\n", p2) 407 | } 408 | 409 | // Output: 410 | // {Title:Example Author:Gary Body:Hello} 411 | // {Title:Example2 Author:Steve Body:Map} 412 | } 413 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/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/DavidHuie/httpq/Godeps/_workspace/src/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 | Dial func() (Conn, error) 99 | 100 | // TestOnBorrow is an optional application supplied function for checking 101 | // the health of an idle connection before the connection is used again by 102 | // the application. Argument t is the time that the connection was returned 103 | // to the pool. If the function returns an error, then the connection is 104 | // closed. 105 | TestOnBorrow func(c Conn, t time.Time) error 106 | 107 | // Maximum number of idle connections in the pool. 108 | MaxIdle int 109 | 110 | // Maximum number of connections allocated by the pool at a given time. 111 | // When zero, there is no limit on the number of connections in the pool. 112 | MaxActive int 113 | 114 | // Close connections after remaining idle for this duration. If the value 115 | // is zero, then idle connections are not closed. Applications should set 116 | // the timeout to a value less than the server's timeout. 117 | IdleTimeout time.Duration 118 | 119 | // If Wait is true and the pool is at the MaxIdle limit, then Get() waits 120 | // for a connection to be returned to the pool before returning. 121 | Wait bool 122 | 123 | // mu protects fields defined below. 124 | mu sync.Mutex 125 | cond *sync.Cond 126 | closed bool 127 | active int 128 | 129 | // Stack of idleConn with most recently used at the front. 130 | idle list.List 131 | } 132 | 133 | type idleConn struct { 134 | c Conn 135 | t time.Time 136 | } 137 | 138 | // NewPool creates a new pool. This function is deprecated. Applications should 139 | // initialize the Pool fields directly as shown in example. 140 | func NewPool(newFn func() (Conn, error), maxIdle int) *Pool { 141 | return &Pool{Dial: newFn, MaxIdle: maxIdle} 142 | } 143 | 144 | // Get gets a connection. The application must close the returned connection. 145 | // This method always returns a valid connection so that applications can defer 146 | // error handling to the first use of the connection. If there is an error 147 | // getting an underlying connection, then the connection Err, Do, Send, Flush 148 | // and Receive methods return that error. 149 | func (p *Pool) Get() Conn { 150 | c, err := p.get() 151 | if err != nil { 152 | return errorConnection{err} 153 | } 154 | return &pooledConnection{p: p, c: c} 155 | } 156 | 157 | // ActiveCount returns the number of active connections in the pool. 158 | func (p *Pool) ActiveCount() int { 159 | p.mu.Lock() 160 | active := p.active 161 | p.mu.Unlock() 162 | return active 163 | } 164 | 165 | // Close releases the resources used by the pool. 166 | func (p *Pool) Close() error { 167 | p.mu.Lock() 168 | idle := p.idle 169 | p.idle.Init() 170 | p.closed = true 171 | p.active -= idle.Len() 172 | if p.cond != nil { 173 | p.cond.Broadcast() 174 | } 175 | p.mu.Unlock() 176 | for e := idle.Front(); e != nil; e = e.Next() { 177 | e.Value.(idleConn).c.Close() 178 | } 179 | return nil 180 | } 181 | 182 | // release decrements the active count and signals waiters. The caller must 183 | // hold p.mu during the call. 184 | func (p *Pool) release() { 185 | p.active -= 1 186 | if p.cond != nil { 187 | p.cond.Signal() 188 | } 189 | } 190 | 191 | // get prunes stale connections and returns a connection from the idle list or 192 | // creates a new connection. 193 | func (p *Pool) get() (Conn, error) { 194 | p.mu.Lock() 195 | 196 | // Prune stale connections. 197 | 198 | if timeout := p.IdleTimeout; timeout > 0 { 199 | for i, n := 0, p.idle.Len(); i < n; i++ { 200 | e := p.idle.Back() 201 | if e == nil { 202 | break 203 | } 204 | ic := e.Value.(idleConn) 205 | if ic.t.Add(timeout).After(nowFunc()) { 206 | break 207 | } 208 | p.idle.Remove(e) 209 | p.release() 210 | p.mu.Unlock() 211 | ic.c.Close() 212 | p.mu.Lock() 213 | } 214 | } 215 | 216 | for { 217 | 218 | // Get idle connection. 219 | 220 | for i, n := 0, p.idle.Len(); i < n; i++ { 221 | e := p.idle.Front() 222 | if e == nil { 223 | break 224 | } 225 | ic := e.Value.(idleConn) 226 | p.idle.Remove(e) 227 | test := p.TestOnBorrow 228 | p.mu.Unlock() 229 | if test == nil || test(ic.c, ic.t) == nil { 230 | return ic.c, nil 231 | } 232 | ic.c.Close() 233 | p.mu.Lock() 234 | p.release() 235 | } 236 | 237 | // Check for pool closed before dialing a new connection. 238 | 239 | if p.closed { 240 | p.mu.Unlock() 241 | return nil, errors.New("redigo: get on closed pool") 242 | } 243 | 244 | // Dial new connection if under limit. 245 | 246 | if p.MaxActive == 0 || p.active < p.MaxActive { 247 | dial := p.Dial 248 | p.active += 1 249 | p.mu.Unlock() 250 | c, err := dial() 251 | if err != nil { 252 | p.mu.Lock() 253 | p.release() 254 | p.mu.Unlock() 255 | c = nil 256 | } 257 | return c, err 258 | } 259 | 260 | if !p.Wait { 261 | p.mu.Unlock() 262 | return nil, ErrPoolExhausted 263 | } 264 | 265 | if p.cond == nil { 266 | p.cond = sync.NewCond(&p.mu) 267 | } 268 | p.cond.Wait() 269 | } 270 | } 271 | 272 | func (p *Pool) put(c Conn, forceClose bool) error { 273 | err := c.Err() 274 | p.mu.Lock() 275 | if !p.closed && err == nil && !forceClose { 276 | p.idle.PushFront(idleConn{t: nowFunc(), c: c}) 277 | if p.idle.Len() > p.MaxIdle { 278 | c = p.idle.Remove(p.idle.Back()).(idleConn).c 279 | } else { 280 | c = nil 281 | } 282 | } 283 | 284 | if c == nil { 285 | if p.cond != nil { 286 | p.cond.Signal() 287 | } 288 | p.mu.Unlock() 289 | return nil 290 | } 291 | 292 | p.release() 293 | p.mu.Unlock() 294 | return c.Close() 295 | } 296 | 297 | type pooledConnection struct { 298 | p *Pool 299 | c Conn 300 | state int 301 | } 302 | 303 | var ( 304 | sentinel []byte 305 | sentinelOnce sync.Once 306 | ) 307 | 308 | func initSentinel() { 309 | p := make([]byte, 64) 310 | if _, err := rand.Read(p); err == nil { 311 | sentinel = p 312 | } else { 313 | h := sha1.New() 314 | io.WriteString(h, "Oops, rand failed. Use time instead.") 315 | io.WriteString(h, strconv.FormatInt(time.Now().UnixNano(), 10)) 316 | sentinel = h.Sum(nil) 317 | } 318 | } 319 | 320 | func (pc *pooledConnection) Close() error { 321 | c := pc.c 322 | if _, ok := c.(errorConnection); ok { 323 | return nil 324 | } 325 | pc.c = errorConnection{errConnClosed} 326 | 327 | if pc.state&internal.MultiState != 0 { 328 | c.Send("DISCARD") 329 | pc.state &^= (internal.MultiState | internal.WatchState) 330 | } else if pc.state&internal.WatchState != 0 { 331 | c.Send("UNWATCH") 332 | pc.state &^= internal.WatchState 333 | } 334 | if pc.state&internal.SubscribeState != 0 { 335 | c.Send("UNSUBSCRIBE") 336 | c.Send("PUNSUBSCRIBE") 337 | // To detect the end of the message stream, ask the server to echo 338 | // a sentinel value and read until we see that value. 339 | sentinelOnce.Do(initSentinel) 340 | c.Send("ECHO", sentinel) 341 | c.Flush() 342 | for { 343 | p, err := c.Receive() 344 | if err != nil { 345 | break 346 | } 347 | if p, ok := p.([]byte); ok && bytes.Equal(p, sentinel) { 348 | pc.state &^= internal.SubscribeState 349 | break 350 | } 351 | } 352 | } 353 | c.Do("") 354 | pc.p.put(c, pc.state != 0) 355 | return nil 356 | } 357 | 358 | func (pc *pooledConnection) Err() error { 359 | return pc.c.Err() 360 | } 361 | 362 | func (pc *pooledConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) { 363 | ci := internal.LookupCommandInfo(commandName) 364 | pc.state = (pc.state | ci.Set) &^ ci.Clear 365 | return pc.c.Do(commandName, args...) 366 | } 367 | 368 | func (pc *pooledConnection) Send(commandName string, args ...interface{}) error { 369 | ci := internal.LookupCommandInfo(commandName) 370 | pc.state = (pc.state | ci.Set) &^ ci.Clear 371 | return pc.c.Send(commandName, args...) 372 | } 373 | 374 | func (pc *pooledConnection) Flush() error { 375 | return pc.c.Flush() 376 | } 377 | 378 | func (pc *pooledConnection) Receive() (reply interface{}, err error) { 379 | return pc.c.Receive() 380 | } 381 | 382 | type errorConnection struct{ err error } 383 | 384 | func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err } 385 | func (ec errorConnection) Send(string, ...interface{}) error { return ec.err } 386 | func (ec errorConnection) Err() error { return ec.err } 387 | func (ec errorConnection) Close() error { return ec.err } 388 | func (ec errorConnection) Flush() error { return ec.err } 389 | func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err } 390 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cursor.go: -------------------------------------------------------------------------------- 1 | package bolt 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "sort" 7 | ) 8 | 9 | // Cursor represents an iterator that can traverse over all key/value pairs in a bucket in sorted order. 10 | // Cursors see nested buckets with value == nil. 11 | // Cursors can be obtained from a transaction and are valid as long as the transaction is open. 12 | // 13 | // Changing data while traversing with a cursor may cause it to be invalidated 14 | // and return unexpected keys and/or values. You must reposition your cursor 15 | // after mutating data. 16 | type Cursor struct { 17 | bucket *Bucket 18 | stack []elemRef 19 | } 20 | 21 | // Bucket returns the bucket that this cursor was created from. 22 | func (c *Cursor) Bucket() *Bucket { 23 | return c.bucket 24 | } 25 | 26 | // First moves the cursor to the first item in the bucket and returns its key and value. 27 | // If the bucket is empty then a nil key and value are returned. 28 | func (c *Cursor) First() (key []byte, value []byte) { 29 | _assert(c.bucket.tx.db != nil, "tx closed") 30 | c.stack = c.stack[:0] 31 | p, n := c.bucket.pageNode(c.bucket.root) 32 | c.stack = append(c.stack, elemRef{page: p, node: n, index: 0}) 33 | c.first() 34 | k, v, flags := c.keyValue() 35 | if (flags & uint32(bucketLeafFlag)) != 0 { 36 | return k, nil 37 | } 38 | return k, v 39 | 40 | } 41 | 42 | // Last moves the cursor to the last item in the bucket and returns its key and value. 43 | // If the bucket is empty then a nil key and value are returned. 44 | func (c *Cursor) Last() (key []byte, value []byte) { 45 | _assert(c.bucket.tx.db != nil, "tx closed") 46 | c.stack = c.stack[:0] 47 | p, n := c.bucket.pageNode(c.bucket.root) 48 | ref := elemRef{page: p, node: n} 49 | ref.index = ref.count() - 1 50 | c.stack = append(c.stack, ref) 51 | c.last() 52 | k, v, flags := c.keyValue() 53 | if (flags & uint32(bucketLeafFlag)) != 0 { 54 | return k, nil 55 | } 56 | return k, v 57 | } 58 | 59 | // Next moves the cursor to the next item in the bucket and returns its key and value. 60 | // If the cursor is at the end of the bucket then a nil key and value are returned. 61 | func (c *Cursor) Next() (key []byte, value []byte) { 62 | _assert(c.bucket.tx.db != nil, "tx closed") 63 | k, v, flags := c.next() 64 | if (flags & uint32(bucketLeafFlag)) != 0 { 65 | return k, nil 66 | } 67 | return k, v 68 | } 69 | 70 | // Prev moves the cursor to the previous item in the bucket and returns its key and value. 71 | // If the cursor is at the beginning of the bucket then a nil key and value are returned. 72 | func (c *Cursor) Prev() (key []byte, value []byte) { 73 | _assert(c.bucket.tx.db != nil, "tx closed") 74 | 75 | // Attempt to move back one element until we're successful. 76 | // Move up the stack as we hit the beginning of each page in our stack. 77 | for i := len(c.stack) - 1; i >= 0; i-- { 78 | elem := &c.stack[i] 79 | if elem.index > 0 { 80 | elem.index-- 81 | break 82 | } 83 | c.stack = c.stack[:i] 84 | } 85 | 86 | // If we've hit the end then return nil. 87 | if len(c.stack) == 0 { 88 | return nil, nil 89 | } 90 | 91 | // Move down the stack to find the last element of the last leaf under this branch. 92 | c.last() 93 | k, v, flags := c.keyValue() 94 | if (flags & uint32(bucketLeafFlag)) != 0 { 95 | return k, nil 96 | } 97 | return k, v 98 | } 99 | 100 | // Seek moves the cursor to a given key and returns it. 101 | // If the key does not exist then the next key is used. If no keys 102 | // follow, a nil key is returned. 103 | func (c *Cursor) Seek(seek []byte) (key []byte, value []byte) { 104 | k, v, flags := c.seek(seek) 105 | 106 | // If we ended up after the last element of a page then move to the next one. 107 | if ref := &c.stack[len(c.stack)-1]; ref.index >= ref.count() { 108 | k, v, flags = c.next() 109 | } 110 | 111 | if k == nil { 112 | return nil, nil 113 | } else if (flags & uint32(bucketLeafFlag)) != 0 { 114 | return k, nil 115 | } 116 | return k, v 117 | } 118 | 119 | // Delete removes the current key/value under the cursor from the bucket. 120 | // Delete fails if current key/value is a bucket or if the transaction is not writable. 121 | func (c *Cursor) Delete() error { 122 | if c.bucket.tx.db == nil { 123 | return ErrTxClosed 124 | } else if !c.bucket.Writable() { 125 | return ErrTxNotWritable 126 | } 127 | 128 | key, _, flags := c.keyValue() 129 | // Return an error if current value is a bucket. 130 | if (flags & bucketLeafFlag) != 0 { 131 | return ErrIncompatibleValue 132 | } 133 | c.node().del(key) 134 | 135 | return nil 136 | } 137 | 138 | // seek moves the cursor to a given key and returns it. 139 | // If the key does not exist then the next key is used. 140 | func (c *Cursor) seek(seek []byte) (key []byte, value []byte, flags uint32) { 141 | _assert(c.bucket.tx.db != nil, "tx closed") 142 | 143 | // Start from root page/node and traverse to correct page. 144 | c.stack = c.stack[:0] 145 | c.search(seek, c.bucket.root) 146 | ref := &c.stack[len(c.stack)-1] 147 | 148 | // If the cursor is pointing to the end of page/node then return nil. 149 | if ref.index >= ref.count() { 150 | return nil, nil, 0 151 | } 152 | 153 | // If this is a bucket then return a nil value. 154 | return c.keyValue() 155 | } 156 | 157 | // first moves the cursor to the first leaf element under the last page in the stack. 158 | func (c *Cursor) first() { 159 | for { 160 | // Exit when we hit a leaf page. 161 | var ref = &c.stack[len(c.stack)-1] 162 | if ref.isLeaf() { 163 | break 164 | } 165 | 166 | // Keep adding pages pointing to the first element to the stack. 167 | var pgid pgid 168 | if ref.node != nil { 169 | pgid = ref.node.inodes[ref.index].pgid 170 | } else { 171 | pgid = ref.page.branchPageElement(uint16(ref.index)).pgid 172 | } 173 | p, n := c.bucket.pageNode(pgid) 174 | c.stack = append(c.stack, elemRef{page: p, node: n, index: 0}) 175 | } 176 | } 177 | 178 | // last moves the cursor to the last leaf element under the last page in the stack. 179 | func (c *Cursor) last() { 180 | for { 181 | // Exit when we hit a leaf page. 182 | ref := &c.stack[len(c.stack)-1] 183 | if ref.isLeaf() { 184 | break 185 | } 186 | 187 | // Keep adding pages pointing to the last element in the stack. 188 | var pgid pgid 189 | if ref.node != nil { 190 | pgid = ref.node.inodes[ref.index].pgid 191 | } else { 192 | pgid = ref.page.branchPageElement(uint16(ref.index)).pgid 193 | } 194 | p, n := c.bucket.pageNode(pgid) 195 | 196 | var nextRef = elemRef{page: p, node: n} 197 | nextRef.index = nextRef.count() - 1 198 | c.stack = append(c.stack, nextRef) 199 | } 200 | } 201 | 202 | // next moves to the next leaf element and returns the key and value. 203 | // If the cursor is at the last leaf element then it stays there and returns nil. 204 | func (c *Cursor) next() (key []byte, value []byte, flags uint32) { 205 | // Attempt to move over one element until we're successful. 206 | // Move up the stack as we hit the end of each page in our stack. 207 | var i int 208 | for i = len(c.stack) - 1; i >= 0; i-- { 209 | elem := &c.stack[i] 210 | if elem.index < elem.count()-1 { 211 | elem.index++ 212 | break 213 | } 214 | } 215 | 216 | // If we've hit the root page then stop and return. This will leave the 217 | // cursor on the last element of the last page. 218 | if i == -1 { 219 | return nil, nil, 0 220 | } 221 | 222 | // Otherwise start from where we left off in the stack and find the 223 | // first element of the first leaf page. 224 | c.stack = c.stack[:i+1] 225 | c.first() 226 | return c.keyValue() 227 | } 228 | 229 | // search recursively performs a binary search against a given page/node until it finds a given key. 230 | func (c *Cursor) search(key []byte, pgid pgid) { 231 | p, n := c.bucket.pageNode(pgid) 232 | if p != nil && (p.flags&(branchPageFlag|leafPageFlag)) == 0 { 233 | panic(fmt.Sprintf("invalid page type: %d: %x", p.id, p.flags)) 234 | } 235 | e := elemRef{page: p, node: n} 236 | c.stack = append(c.stack, e) 237 | 238 | // If we're on a leaf page/node then find the specific node. 239 | if e.isLeaf() { 240 | c.nsearch(key) 241 | return 242 | } 243 | 244 | if n != nil { 245 | c.searchNode(key, n) 246 | return 247 | } 248 | c.searchPage(key, p) 249 | } 250 | 251 | func (c *Cursor) searchNode(key []byte, n *node) { 252 | var exact bool 253 | index := sort.Search(len(n.inodes), func(i int) bool { 254 | // TODO(benbjohnson): Optimize this range search. It's a bit hacky right now. 255 | // sort.Search() finds the lowest index where f() != -1 but we need the highest index. 256 | ret := bytes.Compare(n.inodes[i].key, key) 257 | if ret == 0 { 258 | exact = true 259 | } 260 | return ret != -1 261 | }) 262 | if !exact && index > 0 { 263 | index-- 264 | } 265 | c.stack[len(c.stack)-1].index = index 266 | 267 | // Recursively search to the next page. 268 | c.search(key, n.inodes[index].pgid) 269 | } 270 | 271 | func (c *Cursor) searchPage(key []byte, p *page) { 272 | // Binary search for the correct range. 273 | inodes := p.branchPageElements() 274 | 275 | var exact bool 276 | index := sort.Search(int(p.count), func(i int) bool { 277 | // TODO(benbjohnson): Optimize this range search. It's a bit hacky right now. 278 | // sort.Search() finds the lowest index where f() != -1 but we need the highest index. 279 | ret := bytes.Compare(inodes[i].key(), key) 280 | if ret == 0 { 281 | exact = true 282 | } 283 | return ret != -1 284 | }) 285 | if !exact && index > 0 { 286 | index-- 287 | } 288 | c.stack[len(c.stack)-1].index = index 289 | 290 | // Recursively search to the next page. 291 | c.search(key, inodes[index].pgid) 292 | } 293 | 294 | // nsearch searches the leaf node on the top of the stack for a key. 295 | func (c *Cursor) nsearch(key []byte) { 296 | e := &c.stack[len(c.stack)-1] 297 | p, n := e.page, e.node 298 | 299 | // If we have a node then search its inodes. 300 | if n != nil { 301 | index := sort.Search(len(n.inodes), func(i int) bool { 302 | return bytes.Compare(n.inodes[i].key, key) != -1 303 | }) 304 | e.index = index 305 | return 306 | } 307 | 308 | // If we have a page then search its leaf elements. 309 | inodes := p.leafPageElements() 310 | index := sort.Search(int(p.count), func(i int) bool { 311 | return bytes.Compare(inodes[i].key(), key) != -1 312 | }) 313 | e.index = index 314 | } 315 | 316 | // keyValue returns the key and value of the current leaf element. 317 | func (c *Cursor) keyValue() ([]byte, []byte, uint32) { 318 | ref := &c.stack[len(c.stack)-1] 319 | if ref.count() == 0 || ref.index >= ref.count() { 320 | return nil, nil, 0 321 | } 322 | 323 | // Retrieve value from node. 324 | if ref.node != nil { 325 | inode := &ref.node.inodes[ref.index] 326 | return inode.key, inode.value, inode.flags 327 | } 328 | 329 | // Or retrieve value from page. 330 | elem := ref.page.leafPageElement(uint16(ref.index)) 331 | return elem.key(), elem.value(), elem.flags 332 | } 333 | 334 | // node returns the node that the cursor is currently positioned on. 335 | func (c *Cursor) node() *node { 336 | _assert(len(c.stack) > 0, "accessing a node with a zero-length cursor stack") 337 | 338 | // If the top of the stack is a leaf node then just return it. 339 | if ref := &c.stack[len(c.stack)-1]; ref.node != nil && ref.isLeaf() { 340 | return ref.node 341 | } 342 | 343 | // Start from root and traverse down the hierarchy. 344 | var n = c.stack[0].node 345 | if n == nil { 346 | n = c.bucket.node(c.stack[0].page.id, nil) 347 | } 348 | for _, ref := range c.stack[:len(c.stack)-1] { 349 | _assert(!n.isLeaf, "expected branch node") 350 | n = n.childAt(int(ref.index)) 351 | } 352 | _assert(n.isLeaf, "expected leaf node") 353 | return n 354 | } 355 | 356 | // elemRef represents a reference to an element on a given page/node. 357 | type elemRef struct { 358 | page *page 359 | node *node 360 | index int 361 | } 362 | 363 | // isLeaf returns whether the ref is pointing at a leaf page/node. 364 | func (r *elemRef) isLeaf() bool { 365 | if r.node != nil { 366 | return r.node.isLeaf 367 | } 368 | return (r.page.flags & leafPageFlag) != 0 369 | } 370 | 371 | // count returns the number of inodes or page elements. 372 | func (r *elemRef) count() int { 373 | if r.node != nil { 374 | return len(r.node.inodes) 375 | } 376 | return int(r.page.count) 377 | } 378 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/garyburd/redigo/redis/conn.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 | "bytes" 20 | "errors" 21 | "fmt" 22 | "io" 23 | "net" 24 | "strconv" 25 | "sync" 26 | "time" 27 | ) 28 | 29 | // conn is the low-level implementation of Conn 30 | type conn struct { 31 | 32 | // Shared 33 | mu sync.Mutex 34 | pending int 35 | err error 36 | conn net.Conn 37 | 38 | // Read 39 | readTimeout time.Duration 40 | br *bufio.Reader 41 | 42 | // Write 43 | writeTimeout time.Duration 44 | bw *bufio.Writer 45 | 46 | // Scratch space for formatting argument length. 47 | // '*' or '$', length, "\r\n" 48 | lenScratch [32]byte 49 | 50 | // Scratch space for formatting integers and floats. 51 | numScratch [40]byte 52 | } 53 | 54 | // Dial connects to the Redis server at the given network and address. 55 | func Dial(network, address string) (Conn, error) { 56 | dialer := xDialer{} 57 | return dialer.Dial(network, address) 58 | } 59 | 60 | // DialTimeout acts like Dial but takes timeouts for establishing the 61 | // connection to the server, writing a command and reading a reply. 62 | func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error) { 63 | netDialer := net.Dialer{Timeout: connectTimeout} 64 | dialer := xDialer{ 65 | NetDial: netDialer.Dial, 66 | ReadTimeout: readTimeout, 67 | WriteTimeout: writeTimeout, 68 | } 69 | return dialer.Dial(network, address) 70 | } 71 | 72 | // A Dialer specifies options for connecting to a Redis server. 73 | type xDialer struct { 74 | // NetDial specifies the dial function for creating TCP connections. If 75 | // NetDial is nil, then net.Dial is used. 76 | NetDial func(network, addr string) (net.Conn, error) 77 | 78 | // ReadTimeout specifies the timeout for reading a single command 79 | // reply. If ReadTimeout is zero, then no timeout is used. 80 | ReadTimeout time.Duration 81 | 82 | // WriteTimeout specifies the timeout for writing a single command. If 83 | // WriteTimeout is zero, then no timeout is used. 84 | WriteTimeout time.Duration 85 | } 86 | 87 | // Dial connects to the Redis server at address on the named network. 88 | func (d *xDialer) Dial(network, address string) (Conn, error) { 89 | dial := d.NetDial 90 | if dial == nil { 91 | dial = net.Dial 92 | } 93 | netConn, err := dial(network, address) 94 | if err != nil { 95 | return nil, err 96 | } 97 | return &conn{ 98 | conn: netConn, 99 | bw: bufio.NewWriter(netConn), 100 | br: bufio.NewReader(netConn), 101 | readTimeout: d.ReadTimeout, 102 | writeTimeout: d.WriteTimeout, 103 | }, nil 104 | } 105 | 106 | // NewConn returns a new Redigo connection for the given net connection. 107 | func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn { 108 | return &conn{ 109 | conn: netConn, 110 | bw: bufio.NewWriter(netConn), 111 | br: bufio.NewReader(netConn), 112 | readTimeout: readTimeout, 113 | writeTimeout: writeTimeout, 114 | } 115 | } 116 | 117 | func (c *conn) Close() error { 118 | c.mu.Lock() 119 | err := c.err 120 | if c.err == nil { 121 | c.err = errors.New("redigo: closed") 122 | err = c.conn.Close() 123 | } 124 | c.mu.Unlock() 125 | return err 126 | } 127 | 128 | func (c *conn) fatal(err error) error { 129 | c.mu.Lock() 130 | if c.err == nil { 131 | c.err = err 132 | // Close connection to force errors on subsequent calls and to unblock 133 | // other reader or writer. 134 | c.conn.Close() 135 | } 136 | c.mu.Unlock() 137 | return err 138 | } 139 | 140 | func (c *conn) Err() error { 141 | c.mu.Lock() 142 | err := c.err 143 | c.mu.Unlock() 144 | return err 145 | } 146 | 147 | func (c *conn) writeLen(prefix byte, n int) error { 148 | c.lenScratch[len(c.lenScratch)-1] = '\n' 149 | c.lenScratch[len(c.lenScratch)-2] = '\r' 150 | i := len(c.lenScratch) - 3 151 | for { 152 | c.lenScratch[i] = byte('0' + n%10) 153 | i -= 1 154 | n = n / 10 155 | if n == 0 { 156 | break 157 | } 158 | } 159 | c.lenScratch[i] = prefix 160 | _, err := c.bw.Write(c.lenScratch[i:]) 161 | return err 162 | } 163 | 164 | func (c *conn) writeString(s string) error { 165 | c.writeLen('$', len(s)) 166 | c.bw.WriteString(s) 167 | _, err := c.bw.WriteString("\r\n") 168 | return err 169 | } 170 | 171 | func (c *conn) writeBytes(p []byte) error { 172 | c.writeLen('$', len(p)) 173 | c.bw.Write(p) 174 | _, err := c.bw.WriteString("\r\n") 175 | return err 176 | } 177 | 178 | func (c *conn) writeInt64(n int64) error { 179 | return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10)) 180 | } 181 | 182 | func (c *conn) writeFloat64(n float64) error { 183 | return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64)) 184 | } 185 | 186 | func (c *conn) writeCommand(cmd string, args []interface{}) (err error) { 187 | c.writeLen('*', 1+len(args)) 188 | err = c.writeString(cmd) 189 | for _, arg := range args { 190 | if err != nil { 191 | break 192 | } 193 | switch arg := arg.(type) { 194 | case string: 195 | err = c.writeString(arg) 196 | case []byte: 197 | err = c.writeBytes(arg) 198 | case int: 199 | err = c.writeInt64(int64(arg)) 200 | case int64: 201 | err = c.writeInt64(arg) 202 | case float64: 203 | err = c.writeFloat64(arg) 204 | case bool: 205 | if arg { 206 | err = c.writeString("1") 207 | } else { 208 | err = c.writeString("0") 209 | } 210 | case nil: 211 | err = c.writeString("") 212 | default: 213 | var buf bytes.Buffer 214 | fmt.Fprint(&buf, arg) 215 | err = c.writeBytes(buf.Bytes()) 216 | } 217 | } 218 | return err 219 | } 220 | 221 | type protocolError string 222 | 223 | func (pe protocolError) Error() string { 224 | return fmt.Sprintf("redigo: %s (possible server error or unsupported concurrent read by application)", string(pe)) 225 | } 226 | 227 | func (c *conn) readLine() ([]byte, error) { 228 | p, err := c.br.ReadSlice('\n') 229 | if err == bufio.ErrBufferFull { 230 | return nil, protocolError("long response line") 231 | } 232 | if err != nil { 233 | return nil, err 234 | } 235 | i := len(p) - 2 236 | if i < 0 || p[i] != '\r' { 237 | return nil, protocolError("bad response line terminator") 238 | } 239 | return p[:i], nil 240 | } 241 | 242 | // parseLen parses bulk string and array lengths. 243 | func parseLen(p []byte) (int, error) { 244 | if len(p) == 0 { 245 | return -1, protocolError("malformed length") 246 | } 247 | 248 | if p[0] == '-' && len(p) == 2 && p[1] == '1' { 249 | // handle $-1 and $-1 null replies. 250 | return -1, nil 251 | } 252 | 253 | var n int 254 | for _, b := range p { 255 | n *= 10 256 | if b < '0' || b > '9' { 257 | return -1, protocolError("illegal bytes in length") 258 | } 259 | n += int(b - '0') 260 | } 261 | 262 | return n, nil 263 | } 264 | 265 | // parseInt parses an integer reply. 266 | func parseInt(p []byte) (interface{}, error) { 267 | if len(p) == 0 { 268 | return 0, protocolError("malformed integer") 269 | } 270 | 271 | var negate bool 272 | if p[0] == '-' { 273 | negate = true 274 | p = p[1:] 275 | if len(p) == 0 { 276 | return 0, protocolError("malformed integer") 277 | } 278 | } 279 | 280 | var n int64 281 | for _, b := range p { 282 | n *= 10 283 | if b < '0' || b > '9' { 284 | return 0, protocolError("illegal bytes in length") 285 | } 286 | n += int64(b - '0') 287 | } 288 | 289 | if negate { 290 | n = -n 291 | } 292 | return n, nil 293 | } 294 | 295 | var ( 296 | okReply interface{} = "OK" 297 | pongReply interface{} = "PONG" 298 | ) 299 | 300 | func (c *conn) readReply() (interface{}, error) { 301 | line, err := c.readLine() 302 | if err != nil { 303 | return nil, err 304 | } 305 | if len(line) == 0 { 306 | return nil, protocolError("short response line") 307 | } 308 | switch line[0] { 309 | case '+': 310 | switch { 311 | case len(line) == 3 && line[1] == 'O' && line[2] == 'K': 312 | // Avoid allocation for frequent "+OK" response. 313 | return okReply, nil 314 | case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G': 315 | // Avoid allocation in PING command benchmarks :) 316 | return pongReply, nil 317 | default: 318 | return string(line[1:]), nil 319 | } 320 | case '-': 321 | return Error(string(line[1:])), nil 322 | case ':': 323 | return parseInt(line[1:]) 324 | case '$': 325 | n, err := parseLen(line[1:]) 326 | if n < 0 || err != nil { 327 | return nil, err 328 | } 329 | p := make([]byte, n) 330 | _, err = io.ReadFull(c.br, p) 331 | if err != nil { 332 | return nil, err 333 | } 334 | if line, err := c.readLine(); err != nil { 335 | return nil, err 336 | } else if len(line) != 0 { 337 | return nil, protocolError("bad bulk string format") 338 | } 339 | return p, nil 340 | case '*': 341 | n, err := parseLen(line[1:]) 342 | if n < 0 || err != nil { 343 | return nil, err 344 | } 345 | r := make([]interface{}, n) 346 | for i := range r { 347 | r[i], err = c.readReply() 348 | if err != nil { 349 | return nil, err 350 | } 351 | } 352 | return r, nil 353 | } 354 | return nil, protocolError("unexpected response line") 355 | } 356 | 357 | func (c *conn) Send(cmd string, args ...interface{}) error { 358 | c.mu.Lock() 359 | c.pending += 1 360 | c.mu.Unlock() 361 | if c.writeTimeout != 0 { 362 | c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout)) 363 | } 364 | if err := c.writeCommand(cmd, args); err != nil { 365 | return c.fatal(err) 366 | } 367 | return nil 368 | } 369 | 370 | func (c *conn) Flush() error { 371 | if c.writeTimeout != 0 { 372 | c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout)) 373 | } 374 | if err := c.bw.Flush(); err != nil { 375 | return c.fatal(err) 376 | } 377 | return nil 378 | } 379 | 380 | func (c *conn) Receive() (reply interface{}, err error) { 381 | if c.readTimeout != 0 { 382 | c.conn.SetReadDeadline(time.Now().Add(c.readTimeout)) 383 | } 384 | if reply, err = c.readReply(); err != nil { 385 | return nil, c.fatal(err) 386 | } 387 | // When using pub/sub, the number of receives can be greater than the 388 | // number of sends. To enable normal use of the connection after 389 | // unsubscribing from all channels, we do not decrement pending to a 390 | // negative value. 391 | // 392 | // The pending field is decremented after the reply is read to handle the 393 | // case where Receive is called before Send. 394 | c.mu.Lock() 395 | if c.pending > 0 { 396 | c.pending -= 1 397 | } 398 | c.mu.Unlock() 399 | if err, ok := reply.(Error); ok { 400 | return nil, err 401 | } 402 | return 403 | } 404 | 405 | func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) { 406 | c.mu.Lock() 407 | pending := c.pending 408 | c.pending = 0 409 | c.mu.Unlock() 410 | 411 | if cmd == "" && pending == 0 { 412 | return nil, nil 413 | } 414 | 415 | if c.writeTimeout != 0 { 416 | c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout)) 417 | } 418 | 419 | if cmd != "" { 420 | c.writeCommand(cmd, args) 421 | } 422 | 423 | if err := c.bw.Flush(); err != nil { 424 | return nil, c.fatal(err) 425 | } 426 | 427 | if c.readTimeout != 0 { 428 | c.conn.SetReadDeadline(time.Now().Add(c.readTimeout)) 429 | } 430 | 431 | if cmd == "" { 432 | reply := make([]interface{}, pending) 433 | for i := range reply { 434 | r, e := c.readReply() 435 | if e != nil { 436 | return nil, c.fatal(e) 437 | } 438 | reply[i] = r 439 | } 440 | return reply, nil 441 | } 442 | 443 | var err error 444 | var reply interface{} 445 | for i := 0; i <= pending; i++ { 446 | var e error 447 | if reply, e = c.readReply(); e != nil { 448 | return nil, c.fatal(e) 449 | } 450 | if e, ok := reply.(Error); ok && err == nil { 451 | err = e 452 | } 453 | } 454 | return reply, err 455 | } 456 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/cmd/bolt/bench.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "math/rand" 10 | "os" 11 | "runtime" 12 | "runtime/pprof" 13 | "time" 14 | 15 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 16 | ) 17 | 18 | // File handlers for the various profiles. 19 | var cpuprofile, memprofile, blockprofile *os.File 20 | 21 | var benchBucketName = []byte("bench") 22 | 23 | // Bench executes a customizable, synthetic benchmark against Bolt. 24 | func Bench(options *BenchOptions) { 25 | var results BenchResults 26 | 27 | // Validate options. 28 | if options.BatchSize == 0 { 29 | options.BatchSize = options.Iterations 30 | } else if options.Iterations%options.BatchSize != 0 { 31 | fatal("number of iterations must be divisible by the batch size") 32 | } 33 | 34 | // Find temporary location. 35 | path := tempfile() 36 | 37 | if options.Clean { 38 | defer os.Remove(path) 39 | } else { 40 | println("work:", path) 41 | } 42 | 43 | // Create database. 44 | db, err := bolt.Open(path, 0600, nil) 45 | if err != nil { 46 | fatal(err) 47 | return 48 | } 49 | db.NoSync = options.NoSync 50 | defer db.Close() 51 | 52 | // Enable streaming stats. 53 | if options.StatsInterval > 0 { 54 | go printStats(db, options.StatsInterval) 55 | } 56 | 57 | // Start profiling for writes. 58 | if options.ProfileMode == "rw" || options.ProfileMode == "w" { 59 | benchStartProfiling(options) 60 | } 61 | 62 | // Write to the database. 63 | if err := benchWrite(db, options, &results); err != nil { 64 | fatal("bench: write: ", err) 65 | } 66 | 67 | // Stop profiling for writes only. 68 | if options.ProfileMode == "w" { 69 | benchStopProfiling() 70 | } 71 | 72 | // Start profiling for reads. 73 | if options.ProfileMode == "r" { 74 | benchStartProfiling(options) 75 | } 76 | 77 | // Read from the database. 78 | if err := benchRead(db, options, &results); err != nil { 79 | fatal("bench: read: ", err) 80 | } 81 | 82 | // Stop profiling for writes only. 83 | if options.ProfileMode == "rw" || options.ProfileMode == "r" { 84 | benchStopProfiling() 85 | } 86 | 87 | // Print results. 88 | fmt.Fprintf(os.Stderr, "# Write\t%v\t(%v/op)\t(%v op/sec)\n", results.WriteDuration, results.WriteOpDuration(), results.WriteOpsPerSecond()) 89 | fmt.Fprintf(os.Stderr, "# Read\t%v\t(%v/op)\t(%v op/sec)\n", results.ReadDuration, results.ReadOpDuration(), results.ReadOpsPerSecond()) 90 | fmt.Fprintln(os.Stderr, "") 91 | } 92 | 93 | // Writes to the database. 94 | func benchWrite(db *bolt.DB, options *BenchOptions, results *BenchResults) error { 95 | var err error 96 | var t = time.Now() 97 | 98 | switch options.WriteMode { 99 | case "seq": 100 | err = benchWriteSequential(db, options, results) 101 | case "rnd": 102 | err = benchWriteRandom(db, options, results) 103 | case "seq-nest": 104 | err = benchWriteSequentialNested(db, options, results) 105 | case "rnd-nest": 106 | err = benchWriteRandomNested(db, options, results) 107 | default: 108 | return fmt.Errorf("invalid write mode: %s", options.WriteMode) 109 | } 110 | 111 | results.WriteDuration = time.Since(t) 112 | 113 | return err 114 | } 115 | 116 | func benchWriteSequential(db *bolt.DB, options *BenchOptions, results *BenchResults) error { 117 | var i = uint32(0) 118 | return benchWriteWithSource(db, options, results, func() uint32 { i++; return i }) 119 | } 120 | 121 | func benchWriteRandom(db *bolt.DB, options *BenchOptions, results *BenchResults) error { 122 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 123 | return benchWriteWithSource(db, options, results, func() uint32 { return r.Uint32() }) 124 | } 125 | 126 | func benchWriteSequentialNested(db *bolt.DB, options *BenchOptions, results *BenchResults) error { 127 | var i = uint32(0) 128 | return benchWriteNestedWithSource(db, options, results, func() uint32 { i++; return i }) 129 | } 130 | 131 | func benchWriteRandomNested(db *bolt.DB, options *BenchOptions, results *BenchResults) error { 132 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 133 | return benchWriteNestedWithSource(db, options, results, func() uint32 { return r.Uint32() }) 134 | } 135 | 136 | func benchWriteWithSource(db *bolt.DB, options *BenchOptions, results *BenchResults, keySource func() uint32) error { 137 | results.WriteOps = options.Iterations 138 | 139 | for i := 0; i < options.Iterations; i += options.BatchSize { 140 | err := db.Update(func(tx *bolt.Tx) error { 141 | b, _ := tx.CreateBucketIfNotExists(benchBucketName) 142 | b.FillPercent = options.FillPercent 143 | 144 | for j := 0; j < options.BatchSize; j++ { 145 | var key = make([]byte, options.KeySize) 146 | var value = make([]byte, options.ValueSize) 147 | binary.BigEndian.PutUint32(key, keySource()) 148 | if err := b.Put(key, value); err != nil { 149 | return err 150 | } 151 | } 152 | 153 | return nil 154 | }) 155 | if err != nil { 156 | return err 157 | } 158 | } 159 | return nil 160 | } 161 | 162 | func benchWriteNestedWithSource(db *bolt.DB, options *BenchOptions, results *BenchResults, keySource func() uint32) error { 163 | results.WriteOps = options.Iterations 164 | 165 | for i := 0; i < options.Iterations; i += options.BatchSize { 166 | err := db.Update(func(tx *bolt.Tx) error { 167 | top, _ := tx.CreateBucketIfNotExists(benchBucketName) 168 | top.FillPercent = options.FillPercent 169 | 170 | var name = make([]byte, options.KeySize) 171 | binary.BigEndian.PutUint32(name, keySource()) 172 | b, _ := top.CreateBucketIfNotExists(name) 173 | b.FillPercent = options.FillPercent 174 | 175 | for j := 0; j < options.BatchSize; j++ { 176 | var key = make([]byte, options.KeySize) 177 | var value = make([]byte, options.ValueSize) 178 | binary.BigEndian.PutUint32(key, keySource()) 179 | if err := b.Put(key, value); err != nil { 180 | return err 181 | } 182 | } 183 | 184 | return nil 185 | }) 186 | if err != nil { 187 | return err 188 | } 189 | } 190 | return nil 191 | } 192 | 193 | // Reads from the database. 194 | func benchRead(db *bolt.DB, options *BenchOptions, results *BenchResults) error { 195 | var err error 196 | var t = time.Now() 197 | 198 | switch options.ReadMode { 199 | case "seq": 200 | if options.WriteMode == "seq-nest" || options.WriteMode == "rnd-nest" { 201 | err = benchReadSequentialNested(db, options, results) 202 | } else { 203 | err = benchReadSequential(db, options, results) 204 | } 205 | default: 206 | return fmt.Errorf("invalid read mode: %s", options.ReadMode) 207 | } 208 | 209 | results.ReadDuration = time.Since(t) 210 | 211 | return err 212 | } 213 | 214 | func benchReadSequential(db *bolt.DB, options *BenchOptions, results *BenchResults) error { 215 | return db.View(func(tx *bolt.Tx) error { 216 | var t = time.Now() 217 | 218 | for { 219 | c := tx.Bucket(benchBucketName).Cursor() 220 | var count int 221 | for k, v := c.First(); k != nil; k, v = c.Next() { 222 | if v == nil { 223 | return errors.New("invalid value") 224 | } 225 | count++ 226 | } 227 | 228 | if options.WriteMode == "seq" && count != options.Iterations { 229 | return fmt.Errorf("read seq: iter mismatch: expected %d, got %d", options.Iterations, count) 230 | } 231 | 232 | results.ReadOps += count 233 | 234 | // Make sure we do this for at least a second. 235 | if time.Since(t) >= time.Second { 236 | break 237 | } 238 | } 239 | 240 | return nil 241 | }) 242 | } 243 | 244 | func benchReadSequentialNested(db *bolt.DB, options *BenchOptions, results *BenchResults) error { 245 | return db.View(func(tx *bolt.Tx) error { 246 | var t = time.Now() 247 | 248 | for { 249 | var count int 250 | var top = tx.Bucket(benchBucketName) 251 | top.ForEach(func(name, _ []byte) error { 252 | c := top.Bucket(name).Cursor() 253 | for k, v := c.First(); k != nil; k, v = c.Next() { 254 | if v == nil { 255 | return errors.New("invalid value") 256 | } 257 | count++ 258 | } 259 | return nil 260 | }) 261 | 262 | if options.WriteMode == "seq-nest" && count != options.Iterations { 263 | return fmt.Errorf("read seq-nest: iter mismatch: expected %d, got %d", options.Iterations, count) 264 | } 265 | 266 | results.ReadOps += count 267 | 268 | // Make sure we do this for at least a second. 269 | if time.Since(t) >= time.Second { 270 | break 271 | } 272 | } 273 | 274 | return nil 275 | }) 276 | } 277 | 278 | // Starts all profiles set on the options. 279 | func benchStartProfiling(options *BenchOptions) { 280 | var err error 281 | 282 | // Start CPU profiling. 283 | if options.CPUProfile != "" { 284 | cpuprofile, err = os.Create(options.CPUProfile) 285 | if err != nil { 286 | fatalf("bench: could not create cpu profile %q: %v", options.CPUProfile, err) 287 | } 288 | pprof.StartCPUProfile(cpuprofile) 289 | } 290 | 291 | // Start memory profiling. 292 | if options.MemProfile != "" { 293 | memprofile, err = os.Create(options.MemProfile) 294 | if err != nil { 295 | fatalf("bench: could not create memory profile %q: %v", options.MemProfile, err) 296 | } 297 | runtime.MemProfileRate = 4096 298 | } 299 | 300 | // Start fatal profiling. 301 | if options.BlockProfile != "" { 302 | blockprofile, err = os.Create(options.BlockProfile) 303 | if err != nil { 304 | fatalf("bench: could not create block profile %q: %v", options.BlockProfile, err) 305 | } 306 | runtime.SetBlockProfileRate(1) 307 | } 308 | } 309 | 310 | // Stops all profiles. 311 | func benchStopProfiling() { 312 | if cpuprofile != nil { 313 | pprof.StopCPUProfile() 314 | cpuprofile.Close() 315 | cpuprofile = nil 316 | } 317 | 318 | if memprofile != nil { 319 | pprof.Lookup("heap").WriteTo(memprofile, 0) 320 | memprofile.Close() 321 | memprofile = nil 322 | } 323 | 324 | if blockprofile != nil { 325 | pprof.Lookup("block").WriteTo(blockprofile, 0) 326 | blockprofile.Close() 327 | blockprofile = nil 328 | runtime.SetBlockProfileRate(0) 329 | } 330 | } 331 | 332 | // Continuously prints stats on the database at given intervals. 333 | func printStats(db *bolt.DB, interval time.Duration) { 334 | var prevStats = db.Stats() 335 | var encoder = json.NewEncoder(os.Stdout) 336 | 337 | for { 338 | // Wait for the stats interval. 339 | time.Sleep(interval) 340 | 341 | // Retrieve new stats and find difference from previous iteration. 342 | var stats = db.Stats() 343 | var diff = stats.Sub(&prevStats) 344 | 345 | // Print as JSON to STDOUT. 346 | if err := encoder.Encode(diff); err != nil { 347 | fatal(err) 348 | } 349 | 350 | // Save stats for next iteration. 351 | prevStats = stats 352 | } 353 | } 354 | 355 | // BenchOptions represents the set of options that can be passed to Bench(). 356 | type BenchOptions struct { 357 | ProfileMode string 358 | WriteMode string 359 | ReadMode string 360 | Iterations int 361 | BatchSize int 362 | KeySize int 363 | ValueSize int 364 | CPUProfile string 365 | MemProfile string 366 | BlockProfile string 367 | StatsInterval time.Duration 368 | FillPercent float64 369 | NoSync bool 370 | Clean bool 371 | } 372 | 373 | // BenchResults represents the performance results of the benchmark. 374 | type BenchResults struct { 375 | WriteOps int 376 | WriteDuration time.Duration 377 | ReadOps int 378 | ReadDuration time.Duration 379 | } 380 | 381 | // Returns the duration for a single write operation. 382 | func (r *BenchResults) WriteOpDuration() time.Duration { 383 | if r.WriteOps == 0 { 384 | return 0 385 | } 386 | return r.WriteDuration / time.Duration(r.WriteOps) 387 | } 388 | 389 | // Returns average number of write operations that can be performed per second. 390 | func (r *BenchResults) WriteOpsPerSecond() int { 391 | var op = r.WriteOpDuration() 392 | if op == 0 { 393 | return 0 394 | } 395 | return int(time.Second) / int(op) 396 | } 397 | 398 | // Returns the duration for a single read operation. 399 | func (r *BenchResults) ReadOpDuration() time.Duration { 400 | if r.ReadOps == 0 { 401 | return 0 402 | } 403 | return r.ReadDuration / time.Duration(r.ReadOps) 404 | } 405 | 406 | // Returns average number of read operations that can be performed per second. 407 | func (r *BenchResults) ReadOpsPerSecond() int { 408 | var op = r.ReadOpDuration() 409 | if op == 0 { 410 | return 0 411 | } 412 | return int(time.Second) / int(op) 413 | } 414 | 415 | // tempfile returns a temporary file path. 416 | func tempfile() string { 417 | f, _ := ioutil.TempFile("", "bolt-bench-") 418 | f.Close() 419 | os.Remove(f.Name()) 420 | return f.Name() 421 | } 422 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/boltdb/bolt/tx_test.go: -------------------------------------------------------------------------------- 1 | package bolt_test 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "testing" 8 | 9 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/boltdb/bolt" 10 | ) 11 | 12 | // Ensure that committing a closed transaction returns an error. 13 | func TestTx_Commit_Closed(t *testing.T) { 14 | db := NewTestDB() 15 | defer db.Close() 16 | tx, _ := db.Begin(true) 17 | tx.CreateBucket([]byte("foo")) 18 | ok(t, tx.Commit()) 19 | equals(t, tx.Commit(), bolt.ErrTxClosed) 20 | } 21 | 22 | // Ensure that rolling back a closed transaction returns an error. 23 | func TestTx_Rollback_Closed(t *testing.T) { 24 | db := NewTestDB() 25 | defer db.Close() 26 | tx, _ := db.Begin(true) 27 | ok(t, tx.Rollback()) 28 | equals(t, tx.Rollback(), bolt.ErrTxClosed) 29 | } 30 | 31 | // Ensure that committing a read-only transaction returns an error. 32 | func TestTx_Commit_ReadOnly(t *testing.T) { 33 | db := NewTestDB() 34 | defer db.Close() 35 | tx, _ := db.Begin(false) 36 | equals(t, tx.Commit(), bolt.ErrTxNotWritable) 37 | } 38 | 39 | // Ensure that a transaction can retrieve a cursor on the root bucket. 40 | func TestTx_Cursor(t *testing.T) { 41 | db := NewTestDB() 42 | defer db.Close() 43 | db.Update(func(tx *bolt.Tx) error { 44 | tx.CreateBucket([]byte("widgets")) 45 | tx.CreateBucket([]byte("woojits")) 46 | c := tx.Cursor() 47 | 48 | k, v := c.First() 49 | equals(t, "widgets", string(k)) 50 | assert(t, v == nil, "") 51 | 52 | k, v = c.Next() 53 | equals(t, "woojits", string(k)) 54 | assert(t, v == nil, "") 55 | 56 | k, v = c.Next() 57 | assert(t, k == nil, "") 58 | assert(t, v == nil, "") 59 | 60 | return nil 61 | }) 62 | } 63 | 64 | // Ensure that creating a bucket with a read-only transaction returns an error. 65 | func TestTx_CreateBucket_ReadOnly(t *testing.T) { 66 | db := NewTestDB() 67 | defer db.Close() 68 | db.View(func(tx *bolt.Tx) error { 69 | b, err := tx.CreateBucket([]byte("foo")) 70 | assert(t, b == nil, "") 71 | equals(t, bolt.ErrTxNotWritable, err) 72 | return nil 73 | }) 74 | } 75 | 76 | // Ensure that creating a bucket on a closed transaction returns an error. 77 | func TestTx_CreateBucket_Closed(t *testing.T) { 78 | db := NewTestDB() 79 | defer db.Close() 80 | tx, _ := db.Begin(true) 81 | tx.Commit() 82 | b, err := tx.CreateBucket([]byte("foo")) 83 | assert(t, b == nil, "") 84 | equals(t, bolt.ErrTxClosed, err) 85 | } 86 | 87 | // Ensure that a Tx can retrieve a bucket. 88 | func TestTx_Bucket(t *testing.T) { 89 | db := NewTestDB() 90 | defer db.Close() 91 | db.Update(func(tx *bolt.Tx) error { 92 | tx.CreateBucket([]byte("widgets")) 93 | b := tx.Bucket([]byte("widgets")) 94 | assert(t, b != nil, "") 95 | return nil 96 | }) 97 | } 98 | 99 | // Ensure that a Tx retrieving a non-existent key returns nil. 100 | func TestTx_Get_Missing(t *testing.T) { 101 | db := NewTestDB() 102 | defer db.Close() 103 | db.Update(func(tx *bolt.Tx) error { 104 | tx.CreateBucket([]byte("widgets")) 105 | tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 106 | value := tx.Bucket([]byte("widgets")).Get([]byte("no_such_key")) 107 | assert(t, value == nil, "") 108 | return nil 109 | }) 110 | } 111 | 112 | // Ensure that a bucket can be created and retrieved. 113 | func TestTx_CreateBucket(t *testing.T) { 114 | db := NewTestDB() 115 | defer db.Close() 116 | 117 | // Create a bucket. 118 | db.Update(func(tx *bolt.Tx) error { 119 | b, err := tx.CreateBucket([]byte("widgets")) 120 | assert(t, b != nil, "") 121 | ok(t, err) 122 | return nil 123 | }) 124 | 125 | // Read the bucket through a separate transaction. 126 | db.View(func(tx *bolt.Tx) error { 127 | b := tx.Bucket([]byte("widgets")) 128 | assert(t, b != nil, "") 129 | return nil 130 | }) 131 | } 132 | 133 | // Ensure that a bucket can be created if it doesn't already exist. 134 | func TestTx_CreateBucketIfNotExists(t *testing.T) { 135 | db := NewTestDB() 136 | defer db.Close() 137 | db.Update(func(tx *bolt.Tx) error { 138 | b, err := tx.CreateBucketIfNotExists([]byte("widgets")) 139 | assert(t, b != nil, "") 140 | ok(t, err) 141 | 142 | b, err = tx.CreateBucketIfNotExists([]byte("widgets")) 143 | assert(t, b != nil, "") 144 | ok(t, err) 145 | 146 | b, err = tx.CreateBucketIfNotExists([]byte{}) 147 | assert(t, b == nil, "") 148 | equals(t, bolt.ErrBucketNameRequired, err) 149 | 150 | b, err = tx.CreateBucketIfNotExists(nil) 151 | assert(t, b == nil, "") 152 | equals(t, bolt.ErrBucketNameRequired, err) 153 | return nil 154 | }) 155 | 156 | // Read the bucket through a separate transaction. 157 | db.View(func(tx *bolt.Tx) error { 158 | b := tx.Bucket([]byte("widgets")) 159 | assert(t, b != nil, "") 160 | return nil 161 | }) 162 | } 163 | 164 | // Ensure that a bucket cannot be created twice. 165 | func TestTx_CreateBucket_Exists(t *testing.T) { 166 | db := NewTestDB() 167 | defer db.Close() 168 | // Create a bucket. 169 | db.Update(func(tx *bolt.Tx) error { 170 | b, err := tx.CreateBucket([]byte("widgets")) 171 | assert(t, b != nil, "") 172 | ok(t, err) 173 | return nil 174 | }) 175 | 176 | // Create the same bucket again. 177 | db.Update(func(tx *bolt.Tx) error { 178 | b, err := tx.CreateBucket([]byte("widgets")) 179 | assert(t, b == nil, "") 180 | equals(t, bolt.ErrBucketExists, err) 181 | return nil 182 | }) 183 | } 184 | 185 | // Ensure that a bucket is created with a non-blank name. 186 | func TestTx_CreateBucket_NameRequired(t *testing.T) { 187 | db := NewTestDB() 188 | defer db.Close() 189 | db.Update(func(tx *bolt.Tx) error { 190 | b, err := tx.CreateBucket(nil) 191 | assert(t, b == nil, "") 192 | equals(t, bolt.ErrBucketNameRequired, err) 193 | return nil 194 | }) 195 | } 196 | 197 | // Ensure that a bucket can be deleted. 198 | func TestTx_DeleteBucket(t *testing.T) { 199 | db := NewTestDB() 200 | defer db.Close() 201 | 202 | // Create a bucket and add a value. 203 | db.Update(func(tx *bolt.Tx) error { 204 | tx.CreateBucket([]byte("widgets")) 205 | tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 206 | return nil 207 | }) 208 | 209 | // Delete the bucket and make sure we can't get the value. 210 | db.Update(func(tx *bolt.Tx) error { 211 | ok(t, tx.DeleteBucket([]byte("widgets"))) 212 | assert(t, tx.Bucket([]byte("widgets")) == nil, "") 213 | return nil 214 | }) 215 | 216 | db.Update(func(tx *bolt.Tx) error { 217 | // Create the bucket again and make sure there's not a phantom value. 218 | b, err := tx.CreateBucket([]byte("widgets")) 219 | assert(t, b != nil, "") 220 | ok(t, err) 221 | assert(t, tx.Bucket([]byte("widgets")).Get([]byte("foo")) == nil, "") 222 | return nil 223 | }) 224 | } 225 | 226 | // Ensure that deleting a bucket on a closed transaction returns an error. 227 | func TestTx_DeleteBucket_Closed(t *testing.T) { 228 | db := NewTestDB() 229 | defer db.Close() 230 | tx, _ := db.Begin(true) 231 | tx.Commit() 232 | equals(t, tx.DeleteBucket([]byte("foo")), bolt.ErrTxClosed) 233 | } 234 | 235 | // Ensure that deleting a bucket with a read-only transaction returns an error. 236 | func TestTx_DeleteBucket_ReadOnly(t *testing.T) { 237 | db := NewTestDB() 238 | defer db.Close() 239 | db.View(func(tx *bolt.Tx) error { 240 | equals(t, tx.DeleteBucket([]byte("foo")), bolt.ErrTxNotWritable) 241 | return nil 242 | }) 243 | } 244 | 245 | // Ensure that nothing happens when deleting a bucket that doesn't exist. 246 | func TestTx_DeleteBucket_NotFound(t *testing.T) { 247 | db := NewTestDB() 248 | defer db.Close() 249 | db.Update(func(tx *bolt.Tx) error { 250 | equals(t, bolt.ErrBucketNotFound, tx.DeleteBucket([]byte("widgets"))) 251 | return nil 252 | }) 253 | } 254 | 255 | // Ensure that Tx commit handlers are called after a transaction successfully commits. 256 | func TestTx_OnCommit(t *testing.T) { 257 | var x int 258 | db := NewTestDB() 259 | defer db.Close() 260 | db.Update(func(tx *bolt.Tx) error { 261 | tx.OnCommit(func() { x += 1 }) 262 | tx.OnCommit(func() { x += 2 }) 263 | _, err := tx.CreateBucket([]byte("widgets")) 264 | return err 265 | }) 266 | equals(t, 3, x) 267 | } 268 | 269 | // Ensure that Tx commit handlers are NOT called after a transaction rolls back. 270 | func TestTx_OnCommit_Rollback(t *testing.T) { 271 | var x int 272 | db := NewTestDB() 273 | defer db.Close() 274 | db.Update(func(tx *bolt.Tx) error { 275 | tx.OnCommit(func() { x += 1 }) 276 | tx.OnCommit(func() { x += 2 }) 277 | tx.CreateBucket([]byte("widgets")) 278 | return errors.New("rollback this commit") 279 | }) 280 | equals(t, 0, x) 281 | } 282 | 283 | // Ensure that the database can be copied to a file path. 284 | func TestTx_CopyFile(t *testing.T) { 285 | db := NewTestDB() 286 | defer db.Close() 287 | var dest = tempfile() 288 | db.Update(func(tx *bolt.Tx) error { 289 | tx.CreateBucket([]byte("widgets")) 290 | tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 291 | tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat")) 292 | return nil 293 | }) 294 | 295 | ok(t, db.View(func(tx *bolt.Tx) error { return tx.CopyFile(dest, 0600) })) 296 | 297 | db2, err := bolt.Open(dest, 0600, nil) 298 | ok(t, err) 299 | defer db2.Close() 300 | 301 | db2.View(func(tx *bolt.Tx) error { 302 | equals(t, []byte("bar"), tx.Bucket([]byte("widgets")).Get([]byte("foo"))) 303 | equals(t, []byte("bat"), tx.Bucket([]byte("widgets")).Get([]byte("baz"))) 304 | return nil 305 | }) 306 | } 307 | 308 | type failWriterError struct{} 309 | 310 | func (failWriterError) Error() string { 311 | return "error injected for tests" 312 | } 313 | 314 | type failWriter struct { 315 | // fail after this many bytes 316 | After int 317 | } 318 | 319 | func (f *failWriter) Write(p []byte) (n int, err error) { 320 | n = len(p) 321 | if n > f.After { 322 | n = f.After 323 | err = failWriterError{} 324 | } 325 | f.After -= n 326 | return n, err 327 | } 328 | 329 | // Ensure that Copy handles write errors right. 330 | func TestTx_CopyFile_Error_Meta(t *testing.T) { 331 | db := NewTestDB() 332 | defer db.Close() 333 | db.Update(func(tx *bolt.Tx) error { 334 | tx.CreateBucket([]byte("widgets")) 335 | tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 336 | tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat")) 337 | return nil 338 | }) 339 | 340 | err := db.View(func(tx *bolt.Tx) error { return tx.Copy(&failWriter{}) }) 341 | equals(t, err.Error(), "meta copy: error injected for tests") 342 | } 343 | 344 | // Ensure that Copy handles write errors right. 345 | func TestTx_CopyFile_Error_Normal(t *testing.T) { 346 | db := NewTestDB() 347 | defer db.Close() 348 | db.Update(func(tx *bolt.Tx) error { 349 | tx.CreateBucket([]byte("widgets")) 350 | tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 351 | tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat")) 352 | return nil 353 | }) 354 | 355 | err := db.View(func(tx *bolt.Tx) error { return tx.Copy(&failWriter{3 * db.Info().PageSize}) }) 356 | equals(t, err.Error(), "error injected for tests") 357 | } 358 | 359 | func ExampleTx_Rollback() { 360 | // Open the database. 361 | db, _ := bolt.Open(tempfile(), 0666, nil) 362 | defer os.Remove(db.Path()) 363 | defer db.Close() 364 | 365 | // Create a bucket. 366 | db.Update(func(tx *bolt.Tx) error { 367 | _, err := tx.CreateBucket([]byte("widgets")) 368 | return err 369 | }) 370 | 371 | // Set a value for a key. 372 | db.Update(func(tx *bolt.Tx) error { 373 | return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 374 | }) 375 | 376 | // Update the key but rollback the transaction so it never saves. 377 | tx, _ := db.Begin(true) 378 | b := tx.Bucket([]byte("widgets")) 379 | b.Put([]byte("foo"), []byte("baz")) 380 | tx.Rollback() 381 | 382 | // Ensure that our original value is still set. 383 | db.View(func(tx *bolt.Tx) error { 384 | value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 385 | fmt.Printf("The value for 'foo' is still: %s\n", value) 386 | return nil 387 | }) 388 | 389 | // Output: 390 | // The value for 'foo' is still: bar 391 | } 392 | 393 | func ExampleTx_CopyFile() { 394 | // Open the database. 395 | db, _ := bolt.Open(tempfile(), 0666, nil) 396 | defer os.Remove(db.Path()) 397 | defer db.Close() 398 | 399 | // Create a bucket and a key. 400 | db.Update(func(tx *bolt.Tx) error { 401 | tx.CreateBucket([]byte("widgets")) 402 | tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) 403 | return nil 404 | }) 405 | 406 | // Copy the database to another file. 407 | toFile := tempfile() 408 | db.View(func(tx *bolt.Tx) error { return tx.CopyFile(toFile, 0666) }) 409 | defer os.Remove(toFile) 410 | 411 | // Open the cloned database. 412 | db2, _ := bolt.Open(toFile, 0666, nil) 413 | defer db2.Close() 414 | 415 | // Ensure that the key exists in the copy. 416 | db2.View(func(tx *bolt.Tx) error { 417 | value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 418 | fmt.Printf("The value for 'foo' in the clone is: %s\n", value) 419 | return nil 420 | }) 421 | 422 | // Output: 423 | // The value for 'foo' in the clone is: bar 424 | } 425 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/garyburd/redigo/redis/conn_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 | "bufio" 19 | "bytes" 20 | "math" 21 | "net" 22 | "reflect" 23 | "strings" 24 | "testing" 25 | "time" 26 | 27 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/internal/redistest" 28 | "github.com/DavidHuie/httpq/Godeps/_workspace/src/github.com/garyburd/redigo/redis" 29 | ) 30 | 31 | var writeTests = []struct { 32 | args []interface{} 33 | expected string 34 | }{ 35 | { 36 | []interface{}{"SET", "key", "value"}, 37 | "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n", 38 | }, 39 | { 40 | []interface{}{"SET", "key", "value"}, 41 | "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n", 42 | }, 43 | { 44 | []interface{}{"SET", "key", byte(100)}, 45 | "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n", 46 | }, 47 | { 48 | []interface{}{"SET", "key", 100}, 49 | "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n", 50 | }, 51 | { 52 | []interface{}{"SET", "key", int64(math.MinInt64)}, 53 | "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$20\r\n-9223372036854775808\r\n", 54 | }, 55 | { 56 | []interface{}{"SET", "key", float64(1349673917.939762)}, 57 | "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$21\r\n1.349673917939762e+09\r\n", 58 | }, 59 | { 60 | []interface{}{"SET", "key", ""}, 61 | "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n", 62 | }, 63 | { 64 | []interface{}{"SET", "key", nil}, 65 | "*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n", 66 | }, 67 | { 68 | []interface{}{"ECHO", true, false}, 69 | "*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n", 70 | }, 71 | } 72 | 73 | func TestWrite(t *testing.T) { 74 | for _, tt := range writeTests { 75 | var buf bytes.Buffer 76 | rw := bufio.ReadWriter{Writer: bufio.NewWriter(&buf)} 77 | c := redis.NewConnBufio(rw) 78 | err := c.Send(tt.args[0].(string), tt.args[1:]...) 79 | if err != nil { 80 | t.Errorf("Send(%v) returned error %v", tt.args, err) 81 | continue 82 | } 83 | rw.Flush() 84 | actual := buf.String() 85 | if actual != tt.expected { 86 | t.Errorf("Send(%v) = %q, want %q", tt.args, actual, tt.expected) 87 | } 88 | } 89 | } 90 | 91 | var errorSentinel = &struct{}{} 92 | 93 | var readTests = []struct { 94 | reply string 95 | expected interface{} 96 | }{ 97 | { 98 | "+OK\r\n", 99 | "OK", 100 | }, 101 | { 102 | "+PONG\r\n", 103 | "PONG", 104 | }, 105 | { 106 | "@OK\r\n", 107 | errorSentinel, 108 | }, 109 | { 110 | "$6\r\nfoobar\r\n", 111 | []byte("foobar"), 112 | }, 113 | { 114 | "$-1\r\n", 115 | nil, 116 | }, 117 | { 118 | ":1\r\n", 119 | int64(1), 120 | }, 121 | { 122 | ":-2\r\n", 123 | int64(-2), 124 | }, 125 | { 126 | "*0\r\n", 127 | []interface{}{}, 128 | }, 129 | { 130 | "*-1\r\n", 131 | nil, 132 | }, 133 | { 134 | "*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n", 135 | []interface{}{[]byte("foo"), []byte("bar"), []byte("Hello"), []byte("World")}, 136 | }, 137 | { 138 | "*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n", 139 | []interface{}{[]byte("foo"), nil, []byte("bar")}, 140 | }, 141 | 142 | { 143 | // "x" is not a valid length 144 | "$x\r\nfoobar\r\n", 145 | errorSentinel, 146 | }, 147 | { 148 | // -2 is not a valid length 149 | "$-2\r\n", 150 | errorSentinel, 151 | }, 152 | { 153 | // "x" is not a valid integer 154 | ":x\r\n", 155 | errorSentinel, 156 | }, 157 | { 158 | // missing \r\n following value 159 | "$6\r\nfoobar", 160 | errorSentinel, 161 | }, 162 | { 163 | // short value 164 | "$6\r\nxx", 165 | errorSentinel, 166 | }, 167 | { 168 | // long value 169 | "$6\r\nfoobarx\r\n", 170 | errorSentinel, 171 | }, 172 | } 173 | 174 | func TestRead(t *testing.T) { 175 | for _, tt := range readTests { 176 | rw := bufio.ReadWriter{ 177 | Reader: bufio.NewReader(strings.NewReader(tt.reply)), 178 | Writer: bufio.NewWriter(nil), // writer need to support Flush 179 | } 180 | c := redis.NewConnBufio(rw) 181 | actual, err := c.Receive() 182 | if tt.expected == errorSentinel { 183 | if err == nil { 184 | t.Errorf("Receive(%q) did not return expected error", tt.reply) 185 | } 186 | } else { 187 | if err != nil { 188 | t.Errorf("Receive(%q) returned error %v", tt.reply, err) 189 | continue 190 | } 191 | if !reflect.DeepEqual(actual, tt.expected) { 192 | t.Errorf("Receive(%q) = %v, want %v", tt.reply, actual, tt.expected) 193 | } 194 | } 195 | } 196 | } 197 | 198 | var testCommands = []struct { 199 | args []interface{} 200 | expected interface{} 201 | }{ 202 | { 203 | []interface{}{"PING"}, 204 | "PONG", 205 | }, 206 | { 207 | []interface{}{"SET", "foo", "bar"}, 208 | "OK", 209 | }, 210 | { 211 | []interface{}{"GET", "foo"}, 212 | []byte("bar"), 213 | }, 214 | { 215 | []interface{}{"GET", "nokey"}, 216 | nil, 217 | }, 218 | { 219 | []interface{}{"MGET", "nokey", "foo"}, 220 | []interface{}{nil, []byte("bar")}, 221 | }, 222 | { 223 | []interface{}{"INCR", "mycounter"}, 224 | int64(1), 225 | }, 226 | { 227 | []interface{}{"LPUSH", "mylist", "foo"}, 228 | int64(1), 229 | }, 230 | { 231 | []interface{}{"LPUSH", "mylist", "bar"}, 232 | int64(2), 233 | }, 234 | { 235 | []interface{}{"LRANGE", "mylist", 0, -1}, 236 | []interface{}{[]byte("bar"), []byte("foo")}, 237 | }, 238 | { 239 | []interface{}{"MULTI"}, 240 | "OK", 241 | }, 242 | { 243 | []interface{}{"LRANGE", "mylist", 0, -1}, 244 | "QUEUED", 245 | }, 246 | { 247 | []interface{}{"PING"}, 248 | "QUEUED", 249 | }, 250 | { 251 | []interface{}{"EXEC"}, 252 | []interface{}{ 253 | []interface{}{[]byte("bar"), []byte("foo")}, 254 | "PONG", 255 | }, 256 | }, 257 | } 258 | 259 | func TestDoCommands(t *testing.T) { 260 | c, err := redistest.Dial() 261 | if err != nil { 262 | t.Fatalf("error connection to database, %v", err) 263 | } 264 | defer c.Close() 265 | 266 | for _, cmd := range testCommands { 267 | actual, err := c.Do(cmd.args[0].(string), cmd.args[1:]...) 268 | if err != nil { 269 | t.Errorf("Do(%v) returned error %v", cmd.args, err) 270 | continue 271 | } 272 | if !reflect.DeepEqual(actual, cmd.expected) { 273 | t.Errorf("Do(%v) = %v, want %v", cmd.args, actual, cmd.expected) 274 | } 275 | } 276 | } 277 | 278 | func TestPipelineCommands(t *testing.T) { 279 | c, err := redistest.Dial() 280 | if err != nil { 281 | t.Fatalf("error connection to database, %v", err) 282 | } 283 | defer c.Close() 284 | 285 | for _, cmd := range testCommands { 286 | if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil { 287 | t.Fatalf("Send(%v) returned error %v", cmd.args, err) 288 | } 289 | } 290 | if err := c.Flush(); err != nil { 291 | t.Errorf("Flush() returned error %v", err) 292 | } 293 | for _, cmd := range testCommands { 294 | actual, err := c.Receive() 295 | if err != nil { 296 | t.Fatalf("Receive(%v) returned error %v", cmd.args, err) 297 | } 298 | if !reflect.DeepEqual(actual, cmd.expected) { 299 | t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected) 300 | } 301 | } 302 | } 303 | 304 | func TestBlankCommmand(t *testing.T) { 305 | c, err := redistest.Dial() 306 | if err != nil { 307 | t.Fatalf("error connection to database, %v", err) 308 | } 309 | defer c.Close() 310 | 311 | for _, cmd := range testCommands { 312 | if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil { 313 | t.Fatalf("Send(%v) returned error %v", cmd.args, err) 314 | } 315 | } 316 | reply, err := redis.Values(c.Do("")) 317 | if err != nil { 318 | t.Fatalf("Do() returned error %v", err) 319 | } 320 | if len(reply) != len(testCommands) { 321 | t.Fatalf("len(reply)=%d, want %d", len(reply), len(testCommands)) 322 | } 323 | for i, cmd := range testCommands { 324 | actual := reply[i] 325 | if !reflect.DeepEqual(actual, cmd.expected) { 326 | t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected) 327 | } 328 | } 329 | } 330 | 331 | func TestRecvBeforeSend(t *testing.T) { 332 | c, err := redistest.Dial() 333 | if err != nil { 334 | t.Fatalf("error connection to database, %v", err) 335 | } 336 | defer c.Close() 337 | done := make(chan struct{}) 338 | go func() { 339 | c.Receive() 340 | close(done) 341 | }() 342 | time.Sleep(time.Millisecond) 343 | c.Send("PING") 344 | c.Flush() 345 | <-done 346 | _, err = c.Do("") 347 | if err != nil { 348 | t.Fatalf("error=%v", err) 349 | } 350 | } 351 | 352 | func TestError(t *testing.T) { 353 | c, err := redistest.Dial() 354 | if err != nil { 355 | t.Fatalf("error connection to database, %v", err) 356 | } 357 | defer c.Close() 358 | 359 | c.Do("SET", "key", "val") 360 | _, err = c.Do("HSET", "key", "fld", "val") 361 | if err == nil { 362 | t.Errorf("Expected err for HSET on string key.") 363 | } 364 | if c.Err() != nil { 365 | t.Errorf("Conn has Err()=%v, expect nil", c.Err()) 366 | } 367 | _, err = c.Do("SET", "key", "val") 368 | if err != nil { 369 | t.Errorf("Do(SET, key, val) returned error %v, expected nil.", err) 370 | } 371 | } 372 | 373 | func TestReadDeadline(t *testing.T) { 374 | l, err := net.Listen("tcp", "127.0.0.1:0") 375 | if err != nil { 376 | t.Fatalf("net.Listen returned %v", err) 377 | } 378 | defer l.Close() 379 | 380 | go func() { 381 | for { 382 | c, err := l.Accept() 383 | if err != nil { 384 | return 385 | } 386 | go func() { 387 | time.Sleep(time.Second) 388 | c.Write([]byte("+OK\r\n")) 389 | c.Close() 390 | }() 391 | } 392 | }() 393 | 394 | c1, err := redis.DialTimeout(l.Addr().Network(), l.Addr().String(), 0, time.Millisecond, 0) 395 | if err != nil { 396 | t.Fatalf("redis.Dial returned %v", err) 397 | } 398 | defer c1.Close() 399 | 400 | _, err = c1.Do("PING") 401 | if err == nil { 402 | t.Fatalf("c1.Do() returned nil, expect error") 403 | } 404 | if c1.Err() == nil { 405 | t.Fatalf("c1.Err() = nil, expect error") 406 | } 407 | 408 | c2, err := redis.DialTimeout(l.Addr().Network(), l.Addr().String(), 0, time.Millisecond, 0) 409 | if err != nil { 410 | t.Fatalf("redis.Dial returned %v", err) 411 | } 412 | defer c2.Close() 413 | 414 | c2.Send("PING") 415 | c2.Flush() 416 | _, err = c2.Receive() 417 | if err == nil { 418 | t.Fatalf("c2.Receive() returned nil, expect error") 419 | } 420 | if c2.Err() == nil { 421 | t.Fatalf("c2.Err() = nil, expect error") 422 | } 423 | } 424 | 425 | // Connect to local instance of Redis running on the default port. 426 | func ExampleDial(x int) { 427 | c, err := redis.Dial("tcp", ":6379") 428 | if err != nil { 429 | // handle error 430 | } 431 | defer c.Close() 432 | } 433 | 434 | // TextExecError tests handling of errors in a transaction. See 435 | // http://redis.io/topics/transactions for information on how Redis handles 436 | // errors in a transaction. 437 | func TestExecError(t *testing.T) { 438 | c, err := redistest.Dial() 439 | if err != nil { 440 | t.Fatalf("error connection to database, %v", err) 441 | } 442 | defer c.Close() 443 | 444 | // Execute commands that fail before EXEC is called. 445 | 446 | c.Do("ZADD", "k0", 0, 0) 447 | c.Send("MULTI") 448 | c.Send("NOTACOMMAND", "k0", 0, 0) 449 | c.Send("ZINCRBY", "k0", 0, 0) 450 | v, err := c.Do("EXEC") 451 | if err == nil { 452 | t.Fatalf("EXEC returned values %v, expected error", v) 453 | } 454 | 455 | // Execute commands that fail after EXEC is called. The first command 456 | // returns an error. 457 | 458 | c.Do("ZADD", "k1", 0, 0) 459 | c.Send("MULTI") 460 | c.Send("HSET", "k1", 0, 0) 461 | c.Send("ZINCRBY", "k1", 0, 0) 462 | v, err = c.Do("EXEC") 463 | if err != nil { 464 | t.Fatalf("EXEC returned error %v", err) 465 | } 466 | 467 | vs, err := redis.Values(v, nil) 468 | if err != nil { 469 | t.Fatalf("Values(v) returned error %v", err) 470 | } 471 | 472 | if len(vs) != 2 { 473 | t.Fatalf("len(vs) == %d, want 2", len(vs)) 474 | } 475 | 476 | if _, ok := vs[0].(error); !ok { 477 | t.Fatalf("first result is type %T, expected error", vs[0]) 478 | } 479 | 480 | if _, ok := vs[1].([]byte); !ok { 481 | t.Fatalf("second result is type %T, expected []byte", vs[2]) 482 | } 483 | 484 | // Execute commands that fail after EXEC is called. The second command 485 | // returns an error. 486 | 487 | c.Do("ZADD", "k2", 0, 0) 488 | c.Send("MULTI") 489 | c.Send("ZINCRBY", "k2", 0, 0) 490 | c.Send("HSET", "k2", 0, 0) 491 | v, err = c.Do("EXEC") 492 | if err != nil { 493 | t.Fatalf("EXEC returned error %v", err) 494 | } 495 | 496 | vs, err = redis.Values(v, nil) 497 | if err != nil { 498 | t.Fatalf("Values(v) returned error %v", err) 499 | } 500 | 501 | if len(vs) != 2 { 502 | t.Fatalf("len(vs) == %d, want 2", len(vs)) 503 | } 504 | 505 | if _, ok := vs[0].([]byte); !ok { 506 | t.Fatalf("first result is type %T, expected []byte", vs[0]) 507 | } 508 | 509 | if _, ok := vs[1].(error); !ok { 510 | t.Fatalf("second result is type %T, expected error", vs[2]) 511 | } 512 | } 513 | 514 | func BenchmarkDoEmpty(b *testing.B) { 515 | b.StopTimer() 516 | c, err := redistest.Dial() 517 | if err != nil { 518 | b.Fatal(err) 519 | } 520 | defer c.Close() 521 | b.StartTimer() 522 | for i := 0; i < b.N; i++ { 523 | if _, err := c.Do(""); err != nil { 524 | b.Fatal(err) 525 | } 526 | } 527 | } 528 | 529 | func BenchmarkDoPing(b *testing.B) { 530 | b.StopTimer() 531 | c, err := redistest.Dial() 532 | if err != nil { 533 | b.Fatal(err) 534 | } 535 | defer c.Close() 536 | b.StartTimer() 537 | for i := 0; i < b.N; i++ { 538 | if _, err := c.Do("PING"); err != nil { 539 | b.Fatal(err) 540 | } 541 | } 542 | } 543 | --------------------------------------------------------------------------------